From 30e83d8c520598355b695825c938caa6c291f299 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sun, 27 Sep 2026 11:16:37 +1300 Subject: [PATCH] Element editor: give every primitive a uuid Lines, rectangles, ellipses, arcs, polygons, PLC tables and static texts now save a uuid, as terminals and dynamic texts already did, so a tool can name one part of a symbol. The graphic parts keep it next to their styles, since all of them save through stylesToXml(); a part read from a definition that predates this gets one on the next save. Paste renews them, as it already did for terminals -- and now for dynamic texts too, which were pasted with their source's uuid. A placed element gives its dynamic texts fresh uuids anyway, so only the definition's own identity changes. PartTerminal::uuid() returns the terminal's saved uuid rather than inheriting the graphic-part one it never writes. Co-Authored-By: Claude Opus 5.5 --- .../editor/UndoCommand/pastepartscommand.cpp | 20 +++++++++++++++++-- .../graphicspart/customelementgraphicpart.cpp | 10 ++++++++++ .../graphicspart/customelementgraphicpart.h | 8 ++++++++ .../graphicspart/partdynamictextfield.h | 2 ++ sources/editor/graphicspart/partterminal.cpp | 5 +++++ sources/editor/graphicspart/partterminal.h | 3 +++ sources/editor/graphicspart/parttext.cpp | 6 ++++++ sources/editor/graphicspart/parttext.h | 5 +++++ 8 files changed, 57 insertions(+), 2 deletions(-) diff --git a/sources/editor/UndoCommand/pastepartscommand.cpp b/sources/editor/UndoCommand/pastepartscommand.cpp index f8352c6a6..d01cfc98a 100644 --- a/sources/editor/UndoCommand/pastepartscommand.cpp +++ b/sources/editor/UndoCommand/pastepartscommand.cpp @@ -18,15 +18,17 @@ #include "pastepartscommand.h" #include "../elementview.h" +#include "../graphicspart/partdynamictextfield.h" #include "../graphicspart/partterminal.h" +#include "../graphicspart/parttext.h" /** @brief PastePartsCommand::PastePartsCommand @param view : view where this command work @param content_to_paste : content to paste @param parent : parent undo command - @note all terminal stored in content_to_paste get a new uuid - in the constructor of this class to avoid have several terminal + @note all terminal and primitives stored in content_to_paste get a new + uuid in the constructor of this class to avoid have several parts of an element with the same uuid. */ PastePartsCommand::PastePartsCommand( @@ -40,6 +42,20 @@ PastePartsCommand::PastePartsCommand( if (qgi->type() == PartTerminal::Type) { auto part_terminal = static_cast(qgi); part_terminal->setNewUuid(); + } + //Same for the primitives, whose uuid would otherwise name both + //the original and its copy. + else if (qgi->type() == PartText::Type) { + static_cast(qgi)->setNewUuid(); + } + //Only the definition's own identity: a placed element gives + //each of its dynamic texts a fresh uuid anyway (see + //Element::parseDynamicText()). + else if (qgi->type() == PartDynamicTextField::Type) { + static_cast(qgi)->setNewUuid(); + } + else if (auto graphic_part = dynamic_cast(qgi)) { + graphic_part->setNewUuid(); } m_pasted_content.append(qgi); } diff --git a/sources/editor/graphicspart/customelementgraphicpart.cpp b/sources/editor/graphicspart/customelementgraphicpart.cpp index f19717eae..f052d738d 100644 --- a/sources/editor/graphicspart/customelementgraphicpart.cpp +++ b/sources/editor/graphicspart/customelementgraphicpart.cpp @@ -507,6 +507,9 @@ void CustomElementGraphicPart::stylesToXml(QDomElement &qde) const if (color != color_map.end()) { css_like_styles += color.value(); } qde.setAttribute("style", css_like_styles); + //Every graphic part writes and reads its styles through this pair, + //so its uuid is kept here rather than in six toXml()/fromXml(). + qde.setAttribute("uuid", m_uuid.toString()); qde.setAttribute("antialias", _antialiased ? "true" : "false"); } @@ -520,6 +523,13 @@ void CustomElementGraphicPart::stylesFromXml(const QDomElement &qde) { resetStyles(); + //Absent from definitions written before parts carried one: keep the + //fresh uuid this part was built with, and the next save persists it. + const QUuid uuid(qde.attribute(QStringLiteral("uuid"))); + if (!uuid.isNull()) { + m_uuid = uuid; + } + //Get the list of pair style/value QStringList styles = qde.attribute("style").split(";", Qt::SkipEmptyParts); diff --git a/sources/editor/graphicspart/customelementgraphicpart.h b/sources/editor/graphicspart/customelementgraphicpart.h index 0ac4e69cf..e52d4c269 100644 --- a/sources/editor/graphicspart/customelementgraphicpart.h +++ b/sources/editor/graphicspart/customelementgraphicpart.h @@ -19,6 +19,7 @@ #define CUSTOM_ELEMENT_GRAPHIC_PART_H #include +#include #include "customelementpart.h" class QETElementEditor; @@ -305,6 +306,12 @@ class CustomElementGraphicPart : public QGraphicsObject, public CustomElementPar const QColor &/*color*/) {} virtual void resetAllHandlerColor() {} + //Identity of this primitive inside its element definition, for + //tools that address one part of a drawing (a script, the MCP + //server). Saved with the styles, see stylesToXml(). + QUuid uuid() const {return m_uuid;} + void setNewUuid() {m_uuid = QUuid::createUuid();} + protected: void stylesToXml (QDomElement &) const; void stylesFromXml(const QDomElement &); @@ -323,6 +330,7 @@ class CustomElementGraphicPart : public QGraphicsObject, public CustomElementPar // attributes bool m_hovered; private: + QUuid m_uuid = QUuid::createUuid(); LineStyle _linestyle; LineWeight _lineweight; Filling _filling ; diff --git a/sources/editor/graphicspart/partdynamictextfield.h b/sources/editor/graphicspart/partdynamictextfield.h index 1b730639b..fa0c49fcf 100644 --- a/sources/editor/graphicspart/partdynamictextfield.h +++ b/sources/editor/graphicspart/partdynamictextfield.h @@ -75,6 +75,8 @@ class PartDynamicTextField : public QGraphicsTextItem, public CustomElementPart QString xmlName() const override; static QString xmlTaggName() {return QString("dynamic_text");} bool isUseless() const override {return false;} + QUuid uuid() const {return m_uuid;} + void setNewUuid() {m_uuid = QUuid::createUuid();} QRectF sceneGeometricRect() const override {return sceneBoundingRect();} void startUserTransformation(const QRectF &initial_selection_rect) override; void handleUserTransformation(const QRectF &initial_selection_rect, const QRectF &new_selection_rect) override; diff --git a/sources/editor/graphicspart/partterminal.cpp b/sources/editor/graphicspart/partterminal.cpp index 0b85ba026..168c311b0 100644 --- a/sources/editor/graphicspart/partterminal.cpp +++ b/sources/editor/graphicspart/partterminal.cpp @@ -389,6 +389,11 @@ void PartTerminal::setNewUuid() d -> m_uuid = QUuid::createUuid(); } +QUuid PartTerminal::uuid() const +{ + return d -> m_uuid; +} + void PartTerminal::setShowName(bool show) { if (d->m_show_name == show) return; diff --git a/sources/editor/graphicspart/partterminal.h b/sources/editor/graphicspart/partterminal.h index 1ef413cd6..949671758 100644 --- a/sources/editor/graphicspart/partterminal.h +++ b/sources/editor/graphicspart/partterminal.h @@ -141,6 +141,9 @@ class PartTerminal : public CustomElementGraphicPart void setMasterLabelIndex(int index); void setNewUuid(); + //The terminal's own uuid, the one saved on : hides + //CustomElementGraphicPart::uuid(), which a terminal never saves. + QUuid uuid() const; QRectF labelRect() const; diff --git a/sources/editor/graphicspart/parttext.cpp b/sources/editor/graphicspart/parttext.cpp index 47d3007af..56f7faa93 100644 --- a/sources/editor/graphicspart/parttext.cpp +++ b/sources/editor/graphicspart/parttext.cpp @@ -115,6 +115,11 @@ void PartText::flip(qreal axis_y) { void PartText::fromXml(const QDomElement &xml_element) { bool ok; + const QUuid uuid(xml_element.attribute(QStringLiteral("uuid"))); + if (!uuid.isNull()) { + m_uuid = uuid; + } + if (xml_element.hasAttribute("size")) { int font_size = xml_element.attribute("size").toInt(&ok); if (!ok || font_size < 1) { @@ -169,6 +174,7 @@ const QDomElement PartText::toXml(QDomDocument &xml_document) const xml_element.setAttribute("font", QETUtils::fontToString(font())); xml_element.setAttribute("rotation", QString::number(rot)); xml_element.setAttribute("color", defaultTextColor().name()); + xml_element.setAttribute("uuid", m_uuid.toString()); // Only written when different from the historical behaviour, so // existing .elmt files round-trip byte-identical. diff --git a/sources/editor/graphicspart/parttext.h b/sources/editor/graphicspart/parttext.h index 29609440e..4e985af8d 100644 --- a/sources/editor/graphicspart/parttext.h +++ b/sources/editor/graphicspart/parttext.h @@ -50,6 +50,10 @@ class PartText : public QGraphicsTextItem, public CustomElementPart { private: PartText(const PartText &); + public: + QUuid uuid() const {return m_uuid;} + void setNewUuid() {m_uuid = QUuid::createUuid();} + // methods public: enum { Type = UserType + 1107 }; @@ -100,6 +104,7 @@ class PartText : public QGraphicsTextItem, public CustomElementPart { QRectF boundingRect() const override; private: + QUuid m_uuid = QUuid::createUuid(); QPointF margin() const; void applyLineAlignment(); void prepareAlignment();