From 8f2cd9f039eb24ee9506a0e40edbb394d8e75a0d Mon Sep 17 00:00:00 2001 From: DaTweety <129504390+DaTweety@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:45:07 +0200 Subject: [PATCH] Width handles: read the drag in the text's coordinates as they were at the press The text moves on every step of a drag to keep its alignment point in place. Mapping the mouse with its current transform made the delta grow with every step, so a right or centre aligned text ran away: 90 px of mouse gave +1220 px of width. The probe now drags in 30 steps and checks the width grows by exactly as much as the mouse moved. Reported by ispyisail in the review of #1555. Co-Authored-By: Claude Opus 5.5 --- .../dynamicelementtextitem.cpp | 8 ++++++-- .../qetgraphicsitem/dynamicelementtextitem.h | 1 + tests/qttest/element_text_resize_probe.cpp | 19 ++++++++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.cpp b/sources/qetgraphicsitem/dynamicelementtextitem.cpp index 1c056a2ad..0b8dbf982 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.cpp +++ b/sources/qetgraphicsitem/dynamicelementtextitem.cpp @@ -1050,7 +1050,11 @@ void DynamicElementTextItem::handlerMousePressEvent(QetGraphicsHandlerItem *hand //A concrete baseline for the live drag's delta math, which can't //start from -1. m_resize_baseline_width = (m_resize_original_width < 0) ? frameRect().width() : m_resize_original_width; - m_resize_start_local_x = mapFromScene(event->scenePos()).x(); + //The text moves while it is dragged, to keep its alignment point + //in place: read the mouse in the text's coordinates as they were + //at the press, otherwise the delta grows with every step + m_resize_scene_to_local = sceneTransform().inverted(); + m_resize_start_local_x = m_resize_scene_to_local.map(event->scenePos()).x(); //The point the text is aligned on stays where it is: remember it //in the parent's coordinates, which take the text's rotation @@ -1093,7 +1097,7 @@ QPointF DynamicElementTextItem::resizeAnchor() const */ void DynamicElementTextItem::handlerMouseMoveEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event) { - qreal local_x = mapFromScene(event->scenePos()).x(); + qreal local_x = m_resize_scene_to_local.map(event->scenePos()).x(); qreal delta = local_x - m_resize_start_local_x; if (handle == m_left_resize_handle) delta = -delta; diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.h b/sources/qetgraphicsitem/dynamicelementtextitem.h index c009cc537..275420f36 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.h +++ b/sources/qetgraphicsitem/dynamicelementtextitem.h @@ -216,6 +216,7 @@ class DynamicElementTextItem : public DiagramTextItem qreal m_resize_start_local_x = 0; QPointF m_resize_start_pos, m_resize_fixed_parent; + QTransform m_resize_scene_to_local; QMetaObject::Connection m_resize_handles_con; }; diff --git a/tests/qttest/element_text_resize_probe.cpp b/tests/qttest/element_text_resize_probe.cpp index 4f17bbbc0..ba144873a 100644 --- a/tests/qttest/element_text_resize_probe.cpp +++ b/tests/qttest/element_text_resize_probe.cpp @@ -68,18 +68,26 @@ static QPointF anchor(DynamicElementTextItem *text) return text->mapToScene(QPointF(x, y)); } -/// Drag one handle by dx along the text's own baseline. +/// Drag one handle by dx along the text's own baseline, in 30 steps as a +/// mouse does: the text moves on every step to keep its alignment point, +/// so each step must be read the same way as the first. static void drag(Diagram *scene, DynamicElementTextItem *text, bool left, qreal dx) { QetGraphicsHandlerItem *h = handle(text, left); const QPointF start = h->scenePos(); const QPointF end = start + text->mapToScene(QPointF(dx, 0)) - text->mapToScene(QPointF(0, 0)); mouse(scene, h, QEvent::GraphicsSceneMousePress, start); - mouse(scene, h, QEvent::GraphicsSceneMouseMove, (start + end) / 2); - mouse(scene, h, QEvent::GraphicsSceneMouseMove, end); + for (int i = 1; i <= 30; ++i) + mouse(scene, h, QEvent::GraphicsSceneMouseMove, start + (end - start) * i / 30); mouse(scene, h, QEvent::GraphicsSceneMouseRelease, end); } +/// The width changed by exactly as much as the mouse moved. +static void checkGrowth(DynamicElementTextItem *text, qreal before, qreal moved, const char *message) +{ + check(qAbs(text->textWidth() - before - moved) < 0.5, message); +} + static void checkText(Diagram *scene, Element *element, DynamicElementTextItem *text, Qt::Alignment alignment) { text->setTextWidth(-1); @@ -88,10 +96,11 @@ static void checkText(Diagram *scene, Element *element, DynamicElementTextItem * const QPointF pos = text->pos(); const qreal width = text->boundingRect().width(); const QPointF fixed = anchor(text); + const qreal frame = text->frameRect().width(); // Left handle 30 outwards: wider, the alignment point where it was. drag(scene, text, true, -30); - check(text->boundingRect().width() > width + 10, "the left handle widens the text"); + checkGrowth(text, frame, 30, "the left handle widens the text by as much as the mouse moved"); check(near(anchor(text), fixed), "dragging the left handle keeps the alignment point in place"); scene->undoStack().undo(); check(near(text->pos(), pos), "undo puts the text back where it was"); @@ -103,7 +112,7 @@ static void checkText(Diagram *scene, Element *element, DynamicElementTextItem * // Right handle 30 outwards: the same point stays. element->setSelected(true); drag(scene, text, false, 30); - check(text->boundingRect().width() > width + 10, "the right handle widens the text"); + checkGrowth(text, frame, 30, "the right handle widens the text by as much as the mouse moved"); check(near(anchor(text), fixed), "dragging the right handle keeps the alignment point in place"); scene->undoStack().undo(); check(near(text->pos(), pos), "undo of the right handle puts the text back");