Symbol texts: width and height, each fixed or not

In a symbol text's properties, "Width (fixed)" and the new "Height
(fixed)" each carry a tick box. Not ticked, the field follows the text
and shows the size it has now, ready to be fixed; ticked, it keeps the
size beside the box.

A fixed height is new: the field keeps it whatever is written, and the
lines that do not fit are cut off, neither drawn nor picked, to shorten
a text where room is short. It is saved as text_height on the text,
only when set, and changed through an undoable property command like
the width. Translations for all languages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DaTweety
2026-10-19 13:54:19 +02:00
parent 2f4af977b7
commit 255def181c
42 changed files with 1222 additions and 15 deletions
@@ -175,6 +175,8 @@ QDomElement DynamicElementTextItem::toXml(QDomDocument &dom_doc) const
root_element.setAttribute("uuid", m_uuid.toString());
root_element.setAttribute("frame", m_frame? "true" : "false");
root_element.setAttribute("text_width", QString::number(m_text_width));
if (m_text_height > 0)
root_element.setAttribute(QStringLiteral("text_height"), QString::number(m_text_height));
root_element.setAttribute("font", QETUtils::fontToString(font()));
root_element.setAttribute("keep_visual_rotation", m_keep_visual_rotation ? "true" : "false");
root_element.setAttribute("rotation_point_center", m_rotation_point_center ? "true" : "false");
@@ -262,6 +264,7 @@ void DynamicElementTextItem::fromXml(const QDomElement &dom_elmt)
m_uuid = QUuid(dom_elmt.attribute("uuid", QUuid::createUuid().toString()));
setFrame(dom_elmt.attribute("frame", "false") == "true"? true : false);
setTextWidth(dom_elmt.attribute("text_width", QString::number(-1)).toDouble());
setTextHeight(dom_elmt.attribute(QStringLiteral("text_height"), QStringLiteral("-1")).toDouble());
//Text from
QMetaEnum me = textFromMetaEnum();
@@ -867,7 +870,15 @@ void DynamicElementTextItem::paint(QPainter *painter, const QStyleOptionGraphics
if (m_jump_highlight)
painter->fillRect(boundingRect(), QColor(69, 137, 255, 90));
DiagramTextItem::paint(painter, option, widget);
//A fixed height cuts off the lines that do not fit
if (m_text_height > 0) {
painter->save();
painter->setClipRect(boundingRect(), Qt::IntersectClip);
DiagramTextItem::paint(painter, option, widget);
painter->restore();
} else {
DiagramTextItem::paint(painter, option, widget);
}
if (m_frame)
{
@@ -2069,6 +2080,60 @@ void DynamicElementTextItem::setTextWidth(qreal width)
emit textWidthChanged(width);
}
/**
@brief DynamicElementTextItem::setTextHeight
@param height : the height of the text field, -1 to follow the text.
With a height set, the field keeps it whatever is written: the lines
that do not fit are not drawn, to shorten a text where room is short.
*/
void DynamicElementTextItem::setTextHeight(qreal height)
{
if (height <= 0)
height = -1;
if (qFuzzyCompare(height + 2, m_text_height + 2))
return;
prepareGeometryChange();
m_text_height = height;
update();
emit textHeightChanged(height);
}
/**
@brief DynamicElementTextItem::boundingRect
@return the text's own box, cut to the height set by the user
*/
QRectF DynamicElementTextItem::boundingRect() const
{
QRectF rect = DiagramTextItem::boundingRect();
if (m_text_height > 0)
rect.setHeight(m_text_height);
return rect;
}
/**
@brief DynamicElementTextItem::shape
QGraphicsTextItem answers shape() and contains() from its own box, not
from boundingRect(): cut both to the height set by the user, so that
the lines cut off take no clicks either.
*/
QPainterPath DynamicElementTextItem::shape() const
{
QPainterPath path = DiagramTextItem::shape();
if (m_text_height > 0) {
QPainterPath cut;
cut.addRect(boundingRect());
path = path.intersected(cut);
}
return path;
}
bool DynamicElementTextItem::contains(const QPointF &point) const
{
if (m_text_height > 0 && !boundingRect().contains(point))
return false;
return DiagramTextItem::contains(point);
}
/**
@brief DynamicElementTextItem::fitAutoTextWidth
With no width set by the user, a QTextDocument has no width to centre
@@ -51,6 +51,7 @@ class DynamicElementTextItem : public DiagramTextItem
Q_PROPERTY(QString compositeText READ compositeText WRITE setCompositeText NOTIFY compositeTextChanged)
Q_PROPERTY(bool frame READ frame WRITE setFrame NOTIFY frameChanged)
Q_PROPERTY(qreal textWidth READ textWidth WRITE setTextWidth NOTIFY textWidthChanged)
Q_PROPERTY(qreal textHeight READ textHeight WRITE setTextHeight NOTIFY textHeightChanged)
Q_PROPERTY(bool keepVisualRotation READ keepVisualRotation WRITE setKeepVisualRotation NOTIFY keepVisualRotationChanged)
Q_PROPERTY(bool rotationPointCenter READ rotationPointCenter WRITE setRotationPointCenter NOTIFY rotationPointCenterChanged)
public:
@@ -72,6 +73,7 @@ class DynamicElementTextItem : public DiagramTextItem
void frameChanged(bool frame);
void plainTextChanged();
void textWidthChanged(qreal width);
void textHeightChanged(qreal height);
void keepVisualRotationChanged(bool keep);
void rotationPointCenterChanged(bool center);
@@ -122,6 +124,13 @@ class DynamicElementTextItem : public DiagramTextItem
//centred or right-aligned, but the width is still "auto" (-1).
qreal textWidth() const {return m_text_width;}
void setTextWidth(qreal width);
//A height set by the user, -1 for "auto": lines that do not
//fit are cut off, as a text field of fixed size does
qreal textHeight() const {return m_text_height;}
void setTextHeight(qreal height);
QRectF boundingRect() const override;
QPainterPath shape() const override;
bool contains(const QPointF &point) const override;
void setXref_item(Qt::AlignmentFlag m_exHrefPos, int slave_offset = 0);
void setKeepVisualRotation(bool set);
@@ -204,6 +213,7 @@ class DynamicElementTextItem : public DiagramTextItem
QGraphicsTextItem *m_slave_Xref_item = nullptr;
qreal m_text_width = -1;
bool m_jump_highlight = false;
qreal m_text_height = -1;
/// The text was emptied with its alignment kept (setPlainText())
bool m_emptied_aligned = false;
QPointF m_initial_position;
+72 -14
View File
@@ -48,11 +48,12 @@ static int font_txt_row = 5;
static int color_txt_row = 6;
static int frame_txt_row = 7;
static int width_txt_row = 8;
static int x_txt_row = 9;
static int y_txt_row = 10;
static int rot_txt_row = 11;
static int keep_rot_row = 12;
static int align_txt_row = 13;
static int height_txt_row = 9;
static int x_txt_row = 10;
static int y_txt_row = 11;
static int rot_txt_row = 12;
static int keep_rot_row = 13;
static int align_txt_row = 14;
static int align_grp_row = 0;
static int x_grp_row = 1;
@@ -96,6 +97,27 @@ DynamicElementTextModel::~DynamicElementTextModel()
setConnection(group, false);
}
/**
@brief setSizeItems
Show the width and the height of @a deti: ticked with the size set
when it is fixed, not ticked with the size the field has now when it
follows the text, so that ticking it fixes what one sees.
*/
static void setSizeItems(QStandardItem *width, QStandardItem *height, DynamicElementTextItem *deti)
{
const QRectF box = deti->boundingRect();
if (width) {
const bool fixed = deti->textWidth() > 0;
width->setCheckState(fixed ? Qt::Checked : Qt::Unchecked);
width->setData(qRound(fixed ? deti->textWidth() : box.width()), Qt::EditRole);
}
if (height) {
const bool fixed = deti->textHeight() > 0;
height->setCheckState(fixed ? Qt::Checked : Qt::Unchecked);
height->setData(qRound(fixed ? deti->textHeight() : box.height()), Qt::EditRole);
}
}
/**
@brief DynamicElementTextModel::indexIsInGroup
@param index
@@ -281,20 +303,41 @@ QList<QStandardItem *> DynamicElementTextModel::itemsForText(
qsi_list << frame << frame_a;
qsi->appendRow(qsi_list);
//Width
QStandardItem *width = new QStandardItem(tr("Width"));
//Width and height: ticked, the field keeps the size given beside
//it; not ticked, it follows the text, and the size shown is the
//one it has now, ready to be fixed
QStandardItem *width = new QStandardItem(tr("Width (fixed)"));
width->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
width->setToolTip(tr("Ticked, the text field keeps this width and the text wraps inside it. Not ticked, it is as wide as the text."));
QStandardItem *width_a = new QStandardItem;
width_a->setData(deti->textWidth(), Qt::EditRole);
width_a->setCheckable(true);
width_a->setData(DynamicElementTextModel::textWidth, Qt::UserRole+1);
width_a->setFlags(Qt::ItemIsSelectable
| Qt::ItemIsEnabled
| Qt::ItemIsEditable);
| Qt::ItemIsEditable
| Qt::ItemIsUserCheckable);
qsi_list.clear();
qsi_list << width << width_a;
qsi->appendRow(qsi_list);
QStandardItem *height = new QStandardItem(tr("Height (fixed)"));
height->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
height->setToolTip(tr("Ticked, the text field keeps this height and the lines that do not fit are cut off. Not ticked, it is as high as the text."));
QStandardItem *height_a = new QStandardItem;
height_a->setCheckable(true);
height_a->setData(DynamicElementTextModel::textHeight, Qt::UserRole+1);
height_a->setFlags(Qt::ItemIsSelectable
| Qt::ItemIsEnabled
| Qt::ItemIsEditable
| Qt::ItemIsUserCheckable);
qsi_list.clear();
qsi_list << height << height_a;
qsi->appendRow(qsi_list);
setSizeItems(width_a, height_a, deti);
if(deti->parentGroup() == nullptr)
{
@@ -587,13 +630,24 @@ QUndoCommand *DynamicElementTextModel::undoForEditedText(
quc->setText(tr("Modify the frame of an element text"));
}
qreal text_width = text_qsi->child(width_txt_row, 1)->data(Qt::EditRole).toDouble();
QStandardItem *width_item = text_qsi->child(width_txt_row, 1);
qreal text_width = width_item->checkState() == Qt::Checked
? width_item->data(Qt::EditRole).toDouble() : -1;
if(text_width != deti->textWidth())
{
QPropertyUndoCommand *quc = new QPropertyUndoCommand(deti, "textWidth", QVariant(deti->textWidth()), QVariant(text_width), undo);
quc->setAnimated(true, false);
quc->setText(tr("Change the width of an element text"));
}
QStandardItem *height_item = text_qsi->child(height_txt_row, 1);
qreal text_height = height_item->checkState() == Qt::Checked
? height_item->data(Qt::EditRole).toDouble() : -1;
if(text_height != deti->textHeight())
{
QPropertyUndoCommand *quc = new QPropertyUndoCommand(deti, "textHeight", QVariant(deti->textHeight()), QVariant(text_height), undo);
quc->setText(tr("Change the height of an element text"));
}
//When text is in a group, they're isn't item for position of the text
if(text_qsi->child(x_txt_row,1) && text_qsi->child(y_txt_row,1))
@@ -1388,6 +1442,7 @@ void DynamicElementTextModel::setConnection(DynamicElementTextItem *deti, bool s
connection_list << connect(deti, &DynamicElementTextItem::frameChanged, [deti,this](){this->updateDataFromText(deti, frame);});
connection_list << connect(deti, &DynamicElementTextItem::rotationChanged, [deti,this](){this->updateDataFromText(deti, rotation);});
connection_list << connect(deti, &DynamicElementTextItem::textWidthChanged,[deti,this](){this->updateDataFromText(deti, textWidth);});
connection_list << connect(deti, &DynamicElementTextItem::textHeightChanged,[deti,this](){this->updateDataFromText(deti, textHeight);});
connection_list << connect(deti, &DynamicElementTextItem::compositeTextChanged, [deti, this]() {this->updateDataFromText(deti, compositeText);});
m_hash_text_connect.insert(deti, connection_list);
}
@@ -1525,15 +1580,16 @@ void DynamicElementTextModel::updateDataFromText(DynamicElementTextItem *deti,
break;
}
case textWidth:
case textHeight:
{
qsi->child(width_txt_row,1)->setData(deti->textWidth(), Qt::EditRole);
setSizeItems(qsi->child(width_txt_row,1), qsi->child(height_txt_row,1), deti);
break;
}
default:break;
}
//Rotating or resizing the text moves its anchor point (Position X/Y)
if ((type == rotation || type == textWidth)
if ((type == rotation || type == textWidth || type == textHeight)
&& qsi->child(x_txt_row,1) && qsi->child(y_txt_row,1))
{
qsi->child(x_txt_row,1)->setData(deti->anchorPos().x(), Qt::EditRole);
@@ -1761,10 +1817,12 @@ QWidget *DynamicTextItemDelegate::createEditor(
return sb;
}
case DynamicElementTextModel::textWidth:
case DynamicElementTextModel::textHeight:
{
QSpinBox *sb = new QSpinBox(parent);
sb->setObjectName("width_spinbox");
sb->setRange(-1, 500);
sb->setObjectName(index.data(Qt::UserRole+1).toInt() == DynamicElementTextModel::textWidth
? "width_spinbox" : "height_spinbox");
sb->setRange(1, 1000);
sb->setFrame(false);
sb->setSuffix(" px");
return sb;
+1
View File
@@ -53,6 +53,7 @@ class DynamicElementTextModel : public QStandardItemModel
rotation,
keepVisualRotation,
textWidth,
textHeight,
grpAlignment,
grpPos,
grpRotation,