From 214b0e3b7d137fa83ca3910169d2b503fdde8567 Mon Sep 17 00:00:00 2001 From: DaTweety <129504390+DaTweety@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:16:27 +0200 Subject: [PATCH] Width handles keep the point the text is aligned on Which point of a symbol's text stays in place while its width is dragged now follows the text's alignment, the nine points of its alignment dialog, rather than the handle: a text aligned top right keeps its top right corner whichever handle is dragged, one centred keeps its middle, and a text that wraps grows away from that point. Co-Authored-By: Claude Opus 5.5 --- .../dynamicelementtextitem.cpp | 40 ++++++----- .../qetgraphicsitem/dynamicelementtextitem.h | 2 +- tests/qttest/element_text_resize_probe.cpp | 71 +++++++++++-------- 3 files changed, 65 insertions(+), 48 deletions(-) diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.cpp b/sources/qetgraphicsitem/dynamicelementtextitem.cpp index 78db79049..1c056a2ad 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.cpp +++ b/sources/qetgraphicsitem/dynamicelementtextitem.cpp @@ -1041,6 +1041,8 @@ void DynamicElementTextItem::updateResizeHandlesPos() */ void DynamicElementTextItem::handlerMousePressEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event) { + Q_UNUSED(handle) + //The actual property value, kept as-is (possibly -1, meaning "auto") //so a later undo restores the exact original state rather than a //synthesized fixed width. @@ -1050,30 +1052,33 @@ void DynamicElementTextItem::handlerMousePressEvent(QetGraphicsHandlerItem *hand 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 edge opposite the dragged handle stays where it is, as in a - //drawing program: remember it in the parent's coordinates, which - //take the text's rotation into account + //The point the text is aligned on stays where it is: remember it + //in the parent's coordinates, which take the text's rotation + //into account m_resize_start_pos = pos(); - m_resize_fixed_parent = mapToParent(resizeAnchor(handle == m_left_resize_handle)); + m_resize_fixed_parent = mapToParent(resizeAnchor()); } /** @brief DynamicElementTextItem::resizeAnchor - @param left_handle : the left width handle is dragged @return the point of the text, in its own coordinates, that a drag on - a width handle keeps in place: on the opposite edge, and at the top, - the middle or the bottom as the text is aligned. A text that wraps onto - more lines while it narrows then grows away from that point, down for - a text aligned at the top, instead of moving into its neighbours. + a width handle keeps in place: the point it is aligned on, one of the + nine of its alignment (left, centre or right; top, middle or bottom). + A text aligned top right keeps its top right corner whichever handle + is dragged, and a text that wraps onto more lines while it narrows + grows away from that point instead of moving into its neighbours. */ -QPointF DynamicElementTextItem::resizeAnchor(bool left_handle) const +QPointF DynamicElementTextItem::resizeAnchor() const { const QRectF br = boundingRect(); - const Qt::Alignment v = alignment() & Qt::AlignVertical_Mask; - const qreal y = (v & Qt::AlignBottom) ? br.bottom() - : (v & Qt::AlignVCenter) ? br.center().y() + const Qt::Alignment a = alignment(); + const qreal x = (a & Qt::AlignRight) ? br.right() + : (a & Qt::AlignHCenter) ? br.center().x() + : br.left(); + const qreal y = (a & Qt::AlignBottom) ? br.bottom() + : (a & Qt::AlignVCenter) ? br.center().y() : br.top(); - return QPointF(left_handle ? br.right() : br.left(), y); + return QPointF(x, y); } /** @@ -1096,10 +1101,9 @@ void DynamicElementTextItem::handlerMouseMoveEvent(QetGraphicsHandlerItem *handl qreal new_width = qMax(m_resize_baseline_width + delta, qreal(10)); setTextWidth(new_width); - //Keep the opposite edge in place, and the top, middle or bottom - //as the text is aligned, whatever the new number of lines - setPos(pos() + m_resize_fixed_parent - - mapToParent(resizeAnchor(handle == m_left_resize_handle))); + //Keep the point the text is aligned on in place, whatever the new + //width and number of lines + setPos(pos() + m_resize_fixed_parent - mapToParent(resizeAnchor())); updateResizeHandlesPos(); } diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.h b/sources/qetgraphicsitem/dynamicelementtextitem.h index 3077282c7..c009cc537 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.h +++ b/sources/qetgraphicsitem/dynamicelementtextitem.h @@ -170,7 +170,7 @@ class DynamicElementTextItem : public DiagramTextItem void addResizeHandles(); void removeResizeHandles(); void updateResizeHandlesPos(); - QPointF resizeAnchor(bool left_handle) const; + QPointF resizeAnchor() const; void fitAutoTextWidth(); void handlerMousePressEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event); void handlerMouseMoveEvent(QetGraphicsHandlerItem *handle, QGraphicsSceneMouseEvent *event); diff --git a/tests/qttest/element_text_resize_probe.cpp b/tests/qttest/element_text_resize_probe.cpp index ee7d02896..4f17bbbc0 100644 --- a/tests/qttest/element_text_resize_probe.cpp +++ b/tests/qttest/element_text_resize_probe.cpp @@ -1,13 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Width handles of a symbol's text, linked against the application's objects. // -// Dragging the left handle of a symbol's text changes its width and keeps -// its right edge in place; dragging the right handle keeps its left edge, -// as in a drawing program. Before, the text always kept its left edge, so -// a drag on the left handle moved the right edge and the text jumped. -// Undo puts back the width and the position together. A text narrowed -// until it wraps grows downwards from its top corner. Checked for an -// upright and a rotated text. +// Dragging a width handle of a symbol's text changes its width and keeps +// the point the text is aligned on in place, one of the nine of its +// alignment: a text aligned top right keeps its top right corner, one +// centred its middle. Before, the text always kept its left edge, so a +// drag on the left handle moved the right edge and the text jumped. A +// text narrowed until it wraps grows away from that point. Undo puts back +// the width and the position together. Checked for four alignments, on +// an upright and on a rotated text. #include #include #include @@ -57,11 +58,14 @@ static QetGraphicsHandlerItem *handle(DynamicElementTextItem *text, bool left) return found; } -/// The top corner of the text on the left or right, in the folio. -static QPointF edge(DynamicElementTextItem *text, bool left) +/// The point the text is aligned on, one of its nine, in the folio. +static QPointF anchor(DynamicElementTextItem *text) { const QRectF br = text->boundingRect(); - return text->mapToScene(QPointF(left ? br.left() : br.right(), br.top())); + const Qt::Alignment a = text->alignment(); + const qreal x = (a & Qt::AlignRight) ? br.right() : (a & Qt::AlignHCenter) ? br.center().x() : br.left(); + const qreal y = (a & Qt::AlignBottom) ? br.bottom() : (a & Qt::AlignVCenter) ? br.center().y() : br.top(); + return text->mapToScene(QPointF(x, y)); } /// Drag one handle by dx along the text's own baseline. @@ -76,46 +80,55 @@ static void drag(Diagram *scene, DynamicElementTextItem *text, bool left, qreal mouse(scene, h, QEvent::GraphicsSceneMouseRelease, end); } -static void checkText(Diagram *scene, Element *element, DynamicElementTextItem *text) +static void checkText(Diagram *scene, Element *element, DynamicElementTextItem *text, Qt::Alignment alignment) { + text->setTextWidth(-1); + text->setAlignment(alignment); element->setSelected(true); const QPointF pos = text->pos(); const qreal width = text->boundingRect().width(); - const QPointF right = edge(text, false); + const QPointF fixed = anchor(text); - // Left handle 30 to the left: wider, right edge where it was. + // 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"); - check(near(edge(text, false), right), "dragging the left handle keeps the right edge in place"); - + 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"); check(qAbs(text->boundingRect().width() - width) < 0.01, "undo puts the width back"); scene->undoStack().redo(); - check(near(edge(text, false), right), "redo keeps the right edge in place"); + check(near(anchor(text), fixed), "redo keeps the alignment point in place"); scene->undoStack().undo(); - // Right handle 30 to the right: wider, left edge where it was. + // Right handle 30 outwards: the same point stays. element->setSelected(true); - const QPointF left = edge(text, true); drag(scene, text, false, 30); check(text->boundingRect().width() > width + 10, "the right handle widens the text"); - check(near(edge(text, true), left), "dragging the right handle keeps the left edge in place"); + 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 leaves the position alone"); + check(near(text->pos(), pos), "undo of the right handle puts the text back"); - // Narrowed until it wraps onto more lines with the left handle: the - // text grows downwards from its top right corner, it does not move up - // into what stands above it. + // Narrowed until it wraps onto more lines: it grows away from the + // alignment point, a text aligned at the top downwards, so that it + // does not move into what stands above it. + element->setSelected(true); const qreal height = text->boundingRect().height(); - drag(scene, text, true, width - 25); + drag(scene, text, false, 25 - width); check(text->boundingRect().height() > height + 5, "narrowing the text wraps it onto more lines"); - check(near(edge(text, false), right), "a text that wraps keeps its top right corner in place"); + check(near(anchor(text), fixed), "a text that wraps keeps its alignment point in place"); scene->undoStack().undo(); check(near(text->pos(), pos), "undo puts the wrapped text back"); element->setSelected(false); } +static void checkAll(Diagram *scene, Element *element, DynamicElementTextItem *text) +{ + checkText(scene, element, text, Qt::AlignLeft | Qt::AlignTop); + checkText(scene, element, text, Qt::AlignRight | Qt::AlignTop); + checkText(scene, element, text, Qt::AlignHCenter | Qt::AlignVCenter); + checkText(scene, element, text, Qt::AlignRight | Qt::AlignBottom); +} + int main(int argc, char **argv) { QApplication app(argc, argv); @@ -146,13 +159,13 @@ int main(int argc, char **argv) text->setTextFrom(DynamicElementTextItem::UserText); text->setText(QStringLiteral("Sockets bathroom first floor, 5")); - checkText(scene, element, text); - // The same on a text turned by 90 degrees: the fixed edge is + checkAll(scene, element, text); + // The same on a text turned by 90 degrees: the fixed point is // found along the text's own baseline, not the folio's x axis. text->setRotation(90); - checkText(scene, element, text); + checkAll(scene, element, text); - std::puts("PASS: a symbol text's width handles keep the opposite edge in place, undo restores both"); + std::puts("PASS: a symbol text's width handles keep its alignment point in place, undo restores both"); } catch (const std::exception &error) {