Resize free texts by dragging a corner

A selected free text gets the corner handles of the previous commit:
dragging one sets the width the text wraps to, keeping the opposite
corner in place; double-clicking a handle goes back to the automatic
width. One undo step per drag, on the folio's undo stack. The handles
are hidden while the text is typed in.

DiagramTextItem shows the handles for a subclass that asks for them
(textResizeHandlesWanted()); only free texts do for now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Beat Hangartner
2026-10-05 17:56:22 +02:00
parent 0708338bce
commit 2989f6cb4b
4 changed files with 107 additions and 0 deletions
@@ -22,6 +22,8 @@
#include "../qetapp.h"
#include "../richtext/richtexteditor_p.h"
#include "../textanchor.h"
#include "../utils/qetutils.h"
#include "../QetGraphicsItemModeler/textresizehandles.h"
/**
@brief DiagramTextItem::DiagramTextItem
@@ -335,6 +337,7 @@ void DiagramTextItem::focusInEvent(QFocusEvent *event)
m_previous_html_text = toHtml();
m_previous_text = toPlainText();
refreshTextResizeHandles();
}
/**
@@ -357,6 +360,87 @@ void DiagramTextItem::focusOutEvent(QFocusEvent *event)
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsTextItem::ItemIsFocusable, false);
refreshTextResizeHandles();
}
/**
@brief DiagramTextItem::itemChange
Show or hide the resize handles with the selection.
@param change
@param value
@return
*/
QVariant DiagramTextItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
if (change == QGraphicsItem::ItemSelectedHasChanged)
refreshTextResizeHandles();
else if (change == QGraphicsItem::ItemSceneHasChanged && !scene())
removeTextResizeHandles();
return QGraphicsTextItem::itemChange(change, value);
}
/**
@brief DiagramTextItem::textResizeHandlesWanted
@return true when the corner handles to change the width of this text
should be shown. A text without a "textWidth" property never has them.
*/
bool DiagramTextItem::textResizeHandlesWanted() const
{
return false;
}
/**
@brief DiagramTextItem::isEditing
@return true while the text itself is being typed in
*/
bool DiagramTextItem::isEditing() const
{
return textInteractionFlags() & Qt::TextEditable;
}
/**
@brief DiagramTextItem::refreshTextResizeHandles
Create or remove the corner handles to change the width of this text,
according to textResizeHandlesWanted().
Called from itemChange() and when the edition starts or ends, not from
paint(): moving items from paint() left fragments behind
(qelectrotech#1002).
*/
void DiagramTextItem::refreshTextResizeHandles()
{
const bool wanted = scene() && textResizeHandlesWanted();
if (wanted && !m_resize_handles)
{
m_resize_handles = new TextResizeHandles(this, QETUtils::graphicsHandlerSize(this));
connect(m_resize_handles, &TextResizeHandles::resizeFinished,
this, &DiagramTextItem::pushResizeCommand);
}
else if (!wanted && m_resize_handles) {
removeTextResizeHandles();
}
}
/**
@brief DiagramTextItem::removeTextResizeHandles
*/
void DiagramTextItem::removeTextResizeHandles()
{
delete m_resize_handles;
m_resize_handles = nullptr;
}
/**
@brief DiagramTextItem::pushResizeCommand
Make the width change done with the resize handles undoable. The change
is already applied, live during the drag.
*/
void DiagramTextItem::pushResizeCommand(qreal old_width, qreal new_width,
QPointF old_pos, QPointF new_pos)
{
if (Diagram *diagram_ = diagram())
diagram_->undoStack().push(new TextResizeCommand(this, old_width, new_width,
old_pos, new_pos));
}
/**
+12
View File
@@ -24,6 +24,7 @@
class Diagram;
class QDomElement;
class QDomDocument;
class TextResizeHandles;
/**
This class represents a selectable, movable and editable text field on a
@@ -90,12 +91,16 @@ class DiagramTextItem : public QGraphicsTextItem
void setPlainText(const QString &text);
bool isHtml() const;
void refreshTextResizeHandles();
TextResizeHandles *textResizeHandles() const {return m_resize_handles;}
protected:
void paint(QPainter *,
const QStyleOptionGraphicsItem *,
QWidget *) override;
void focusInEvent(QFocusEvent *) override;
void focusOutEvent(QFocusEvent *) override;
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
void mouseDoubleClickEvent (QGraphicsSceneMouseEvent *event) override;
void mousePressEvent (QGraphicsSceneMouseEvent *event) override;
@@ -110,7 +115,10 @@ class DiagramTextItem : public QGraphicsTextItem
void prepareAlignment();
void finishAlignment();
virtual bool textResizeHandlesWanted() const;
virtual bool hasUserTextWidth() const {return false;}
bool isEditing() const;
void removeTextResizeHandles();
protected:
@@ -128,7 +136,11 @@ class DiagramTextItem : public QGraphicsTextItem
QPointF m_mouse_to_origin_movement;
private:
void pushResizeCommand(qreal old_width, qreal new_width,
QPointF old_pos, QPointF new_pos);
QRectF m_alignment_rect;
TextResizeHandles *m_resize_handles = nullptr;
Qt::Alignment m_alignment = (Qt::AlignTop | Qt::AlignLeft);
};
#endif
@@ -148,6 +148,16 @@ void IndependentTextItem::setTextWidth(qreal width)
emit textWidthChanged(width);
}
/**
@brief IndependentTextItem::textResizeHandlesWanted
@return true when this text is selected and not being typed in: the
corner handles then change its width.
*/
bool IndependentTextItem::textResizeHandlesWanted() const
{
return isSelected() && !isEditing();
}
void IndependentTextItem::focusOutEvent(QFocusEvent *event)
{
DiagramTextItem::focusOutEvent(event);
@@ -63,6 +63,7 @@ class IndependentTextItem : public DiagramTextItem
protected:
void focusOutEvent(QFocusEvent *event) override;
bool textResizeHandlesWanted() const override;
bool hasUserTextWidth() const override {return m_text_width > 0;}
private: