From daa648099bf911f54eab92b3702dccb245b7b84b Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Sat, 10 Oct 2026 09:49:42 +0200 Subject: [PATCH] Terminal strip: save the layout (font, cell sizes...) in the project The default terminal strip layout was rebuilt at every project load, so the font size set in the layout editor was reset to 15 px. Add TerminalStripLayoutPattern::toXml/fromXml and TerminalStripLayoutsHandler::toXml/fromXml, write a element in QETProject::toXml() and read it before the diagrams are loaded. Projects without it keep the defaults. --- .../properties/terminalstriplayoutpattern.cpp | 175 ++++++++++++++++++ .../properties/terminalstriplayoutpattern.h | 6 + .../terminalstriplayoutshandler.cpp | 25 +++ .../properties/terminalstriplayoutshandler.h | 4 + sources/qetproject.cpp | 6 + 5 files changed, 216 insertions(+) diff --git a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.cpp b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.cpp index 2d672f9c4..e11ede4a3 100644 --- a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.cpp +++ b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.cpp @@ -18,6 +18,8 @@ #include "terminalstriplayoutpattern.h" #include "../../../utils/qetutils.h" +#include + TerminalStripLayoutPattern::TerminalStripLayoutPattern() { m_font.setPixelSize(15); @@ -116,3 +118,176 @@ void TerminalStripLayoutPattern::updateTerminalsTextOption() m_xref_text_option.setAlignment(m_xref_text_alignment); m_xref_text_option.setWrapMode(QTextOption::WordWrap); } + +namespace +{ + void rectToXml(QDomElement &element, const QString &name, const QRectF &rect) + { + element.setAttribute(name + QStringLiteral("_x"), rect.x()); + element.setAttribute(name + QStringLiteral("_y"), rect.y()); + element.setAttribute(name + QStringLiteral("_w"), rect.width()); + element.setAttribute(name + QStringLiteral("_h"), rect.height()); + } + + QRectF rectFromXml(const QDomElement &element, const QString &name, const QRectF &default_rect) + { + auto value = [&element, &name](const QString &suffix, qreal default_value) + { + bool ok = false; + const auto v = element.attribute(name + suffix).toDouble(&ok); + return ok ? v : default_value; + }; + + return QRectF(value(QStringLiteral("_x"), default_rect.x()), + value(QStringLiteral("_y"), default_rect.y()), + value(QStringLiteral("_w"), default_rect.width()), + value(QStringLiteral("_h"), default_rect.height())); + } + + qreal realFromXml(const QDomElement &element, const QString &name, qreal default_value) + { + bool ok = false; + const auto v = element.attribute(name).toDouble(&ok); + return ok ? v : default_value; + } + + QString orientationToString(Qt::Orientation orientation) { + return orientation == Qt::Horizontal ? QStringLiteral("horizontal") : QStringLiteral("vertical"); + } + + Qt::Orientation orientationFromXml(const QDomElement &element, const QString &name, Qt::Orientation default_value) + { + const auto v = element.attribute(name); + if (v == QLatin1String("horizontal")) return Qt::Horizontal; + if (v == QLatin1String("vertical")) return Qt::Vertical; + return default_value; + } + + Qt::Alignment alignmentFromXml(const QDomElement &element, const QString &name, Qt::Alignment default_value) + { + bool ok = false; + const auto v = element.attribute(name).toInt(&ok); + return ok && v ? Qt::Alignment(static_cast(v)) : default_value; + } +} + +/** + * @brief TerminalStripLayoutPattern::xmlTagName + * @return the tag name of the xml element used by toXml / fromXml + */ +QString TerminalStripLayoutPattern::xmlTagName() { + return QStringLiteral("terminal_strip_layout"); +} + +/** + * @brief TerminalStripLayoutPattern::toXml + * Save all the values of this layout, including the font. + * The font is saved by hand (family, pixel size, bold, italic) and not with + * QFont::toString() because the format of this string is different between Qt5 and Qt6. + * @param document + * @return the xml element + */ +QDomElement TerminalStripLayoutPattern::toXml(QDomDocument &document) const +{ + auto element = document.createElement(xmlTagName()); + + rectToXml(element, QStringLiteral("header"), m_header_rect); + element.setAttribute(QStringLiteral("header_text_orientation"), orientationToString(m_header_text_orientation)); + element.setAttribute(QStringLiteral("header_text_alignment"), static_cast(m_header_text_alignment)); + + rectToXml(element, QStringLiteral("spacer"), m_spacer_rect); + + element.setAttribute(QStringLiteral("font_family"), m_font.family()); + element.setAttribute(QStringLiteral("font_pixel_size"), m_font.pixelSize()); + element.setAttribute(QStringLiteral("font_bold"), m_font.bold() ? 1 : 0); + element.setAttribute(QStringLiteral("font_italic"), m_font.italic() ? 1 : 0); + + for (auto i = 0; i < m_terminal_rect.size(); ++i) { + rectToXml(element, QStringLiteral("terminal_%1").arg(i), m_terminal_rect.at(i)); + } + + element.setAttribute(QStringLiteral("terminals_text_height"), m_terminals_text_height); + element.setAttribute(QStringLiteral("terminals_text_y"), m_terminals_text_y); + element.setAttribute(QStringLiteral("terminals_text_orientation"), orientationToString(m_terminals_text_orientation)); + element.setAttribute(QStringLiteral("terminals_text_alignment"), static_cast(m_terminals_text_alignment)); + + element.setAttribute(QStringLiteral("xref_text_height"), m_xref_text_height); + element.setAttribute(QStringLiteral("xref_text_y"), m_xref_text_y); + element.setAttribute(QStringLiteral("xref_text_orientation"), orientationToString(m_xref_text_orientation)); + element.setAttribute(QStringLiteral("xref_text_alignment"), static_cast(m_xref_text_alignment)); + + element.setAttribute(QStringLiteral("type_symbol_y"), m_type_symbol_y); + element.setAttribute(QStringLiteral("type_symbol_height"), m_type_symbol_height); + element.setAttribute(QStringLiteral("connection_length"), m_connection_length); + element.setAttribute(QStringLiteral("cable_wire_length"), m_cable_wire_length); + element.setAttribute(QStringLiteral("cable_length"), m_cable_length); + element.setAttribute(QStringLiteral("cable_end_length"), m_cable_end_length); + + element.setAttribute(QStringLiteral("bridge_point_d"), m_bridge_point_d); + for (auto i = 0; i < m_bridge_point_y_offset.size(); ++i) { + element.setAttribute(QStringLiteral("bridge_point_y_offset_%1").arg(i), m_bridge_point_y_offset.at(i)); + } + + return element; +} + +/** + * @brief TerminalStripLayoutPattern::fromXml + * Load the values saved by toXml. A value missing in @a layout_element + * (project saved with an older version) keep the current value of this layout. + * @param layout_element + */ +void TerminalStripLayoutPattern::fromXml(const QDomElement &layout_element) +{ + if (layout_element.isNull()) { + return; + } + + m_header_rect = rectFromXml(layout_element, QStringLiteral("header"), m_header_rect); + m_header_text_orientation = orientationFromXml(layout_element, QStringLiteral("header_text_orientation"), m_header_text_orientation); + setHeaderTextAlignment(alignmentFromXml(layout_element, QStringLiteral("header_text_alignment"), m_header_text_alignment)); + + m_spacer_rect = rectFromXml(layout_element, QStringLiteral("spacer"), m_spacer_rect); + + auto font_ = m_font; + if (layout_element.hasAttribute(QStringLiteral("font_family"))) { + font_.setFamily(layout_element.attribute(QStringLiteral("font_family"))); + } + const auto pixel_size = layout_element.attribute(QStringLiteral("font_pixel_size")).toInt(); + if (pixel_size > 0) { + font_.setPixelSize(pixel_size); + } + if (layout_element.hasAttribute(QStringLiteral("font_bold"))) { + font_.setBold(layout_element.attribute(QStringLiteral("font_bold")).toInt() != 0); + } + if (layout_element.hasAttribute(QStringLiteral("font_italic"))) { + font_.setItalic(layout_element.attribute(QStringLiteral("font_italic")).toInt() != 0); + } + setFont(font_); + + for (auto i = 0; i < m_terminal_rect.size(); ++i) { + m_terminal_rect[i] = rectFromXml(layout_element, QStringLiteral("terminal_%1").arg(i), m_terminal_rect.at(i)); + } + + m_terminals_text_height = realFromXml(layout_element, QStringLiteral("terminals_text_height"), m_terminals_text_height); + m_terminals_text_y = realFromXml(layout_element, QStringLiteral("terminals_text_y"), m_terminals_text_y); + m_terminals_text_orientation = orientationFromXml(layout_element, QStringLiteral("terminals_text_orientation"), m_terminals_text_orientation); + setTerminalsTextAlignment(alignmentFromXml(layout_element, QStringLiteral("terminals_text_alignment"), m_terminals_text_alignment)); + + m_xref_text_height = realFromXml(layout_element, QStringLiteral("xref_text_height"), m_xref_text_height); + m_xref_text_y = realFromXml(layout_element, QStringLiteral("xref_text_y"), m_xref_text_y); + m_xref_text_orientation = orientationFromXml(layout_element, QStringLiteral("xref_text_orientation"), m_xref_text_orientation); + setXrefTextAlignment(alignmentFromXml(layout_element, QStringLiteral("xref_text_alignment"), m_xref_text_alignment)); + + m_type_symbol_y = realFromXml(layout_element, QStringLiteral("type_symbol_y"), m_type_symbol_y); + m_type_symbol_height = realFromXml(layout_element, QStringLiteral("type_symbol_height"), m_type_symbol_height); + m_connection_length = realFromXml(layout_element, QStringLiteral("connection_length"), m_connection_length); + m_cable_wire_length = realFromXml(layout_element, QStringLiteral("cable_wire_length"), m_cable_wire_length); + m_cable_length = realFromXml(layout_element, QStringLiteral("cable_length"), m_cable_length); + m_cable_end_length = realFromXml(layout_element, QStringLiteral("cable_end_length"), m_cable_end_length); + + m_bridge_point_d = realFromXml(layout_element, QStringLiteral("bridge_point_d"), m_bridge_point_d); + for (auto i = 0; i < m_bridge_point_y_offset.size(); ++i) { + m_bridge_point_y_offset[i] = realFromXml(layout_element, QStringLiteral("bridge_point_y_offset_%1").arg(i), m_bridge_point_y_offset.at(i)); + } +} diff --git a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.h b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.h index ee5e4e8ee..b432783b2 100644 --- a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.h +++ b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutpattern.h @@ -18,6 +18,7 @@ #ifndef TERMINALSTRIPLAYOUTPATTERN_H #define TERMINALSTRIPLAYOUTPATTERN_H +#include #include #include #include @@ -103,6 +104,11 @@ class TerminalStripLayoutPattern QUuid m_uuid{QUuid::createUuid()}; QString m_name; + //Save / load of the layout in the project xml + QDomElement toXml(QDomDocument &document) const; + void fromXml(const QDomElement &layout_element); + static QString xmlTagName(); + private: void updateHeaderTextOption(); void updateTerminalsTextOption(); diff --git a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.cpp b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.cpp index 66a0bc92f..1dc905d73 100644 --- a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.cpp +++ b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.cpp @@ -17,6 +17,7 @@ */ #include "terminalstriplayoutshandler.h" +#include #include TerminalStripLayoutsHandler::TerminalStripLayoutsHandler() @@ -30,3 +31,27 @@ QSharedPointer TerminalStripLayoutsHandler::defaultL { return m_default_layout; } + +/** + * @brief TerminalStripLayoutsHandler::toXml + * Append the default layout (font included) as a child of @a parent_element + * @param parent_element + */ +void TerminalStripLayoutsHandler::toXml(QDomElement &parent_element) const +{ + auto document = parent_element.ownerDocument(); + parent_element.appendChild(m_default_layout->toXml(document)); +} + +/** + * @brief TerminalStripLayoutsHandler::fromXml + * Load the default layout from the child of @a parent_element, if any. + * The existing layout object is modified in place because the terminal + * strip items share it. + * @param parent_element + */ +void TerminalStripLayoutsHandler::fromXml(const QDomElement &parent_element) +{ + m_default_layout->fromXml( + parent_element.firstChildElement(TerminalStripLayoutPattern::xmlTagName())); +} diff --git a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.h b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.h index cddb842f2..d9275c4da 100644 --- a/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.h +++ b/sources/TerminalStrip/GraphicsItem/properties/terminalstriplayoutshandler.h @@ -18,6 +18,7 @@ #ifndef TERMINALSTRIPLAYOUTSHANDLER_H #define TERMINALSTRIPLAYOUTSHANDLER_H +#include #include #include @@ -33,6 +34,9 @@ class TerminalStripLayoutsHandler TerminalStripLayoutsHandler(); QSharedPointer defaultLayout(); + void toXml(QDomElement &parent_element) const; + void fromXml(const QDomElement &parent_element); + private: QSet> m_layout_set; QSharedPointer m_default_layout; diff --git a/sources/qetproject.cpp b/sources/qetproject.cpp index d6b070f49..829ffd3b6 100644 --- a/sources/qetproject.cpp +++ b/sources/qetproject.cpp @@ -1587,6 +1587,8 @@ QDomDocument QETProject::toXml() //Write terminal strip to xml if (m_terminal_strip_vector.count()) { + m_project_properties_handler.terminalStripLayoutHandler().toXml(project_root); + auto xml_strip = xml_doc.createElement(QStringLiteral("terminal_strips")); for (auto &strip : m_terminal_strip_vector) { xml_strip.appendChild(strip->toXml(xml_doc)); @@ -2092,6 +2094,10 @@ void QETProject::readProjectXml(QDomDocument &xml_project) readElementsCollectionXml(xml_project); const qint64 elements_ms = phase_timer.restart(); + //Load the layout of the terminal strip (font, size of cells...), + //before the diagrams because the terminal strip items use it + m_project_properties_handler.terminalStripLayoutHandler().fromXml(xml_project.documentElement()); + //Load the diagrams readDiagramsXml(xml_project); const qint64 diagrams_ms = phase_timer.restart();