From edf761c2b6ab48cef331cd7ed9238a08fe711f3b Mon Sep 17 00:00:00 2001 From: Kellermorph Date: Thu, 22 Oct 2026 12:51:50 +0200 Subject: [PATCH] Add a Show name box to the selection properties of a terminal The box hides the name of the terminal on the sheet only: the element keeps all of its data, and with the labelling split the terminal keeps its number, drawn once - in its own field when the symbol has one, alone in the label field when it has not. Only a terminal may have its name undrawn: the box is shown for the element type Terminal alone, and Element::labelShown() obeys the stored information for a terminal only. The choice is stored as the element information "label_shown" = "false" and only that, so files of every element which still draws its name stay exactly as they are. Multi-selection: the flag appears for terminals only and counts an absent information as "shown", so a mixed selection shows the box ticked only when every terminal has it ticked. Includes four test cases: hidden name keeps the stored data, keeps the number without a number field, leaves the number to its own field, and a non-terminal draws its name whatever the file says. --- sources/qetgraphicsitem/element.cpp | 46 ++++++++- sources/qetgraphicsitem/element.h | 1 + sources/ui/elementinfowidget.cpp | 52 +++++++++- sources/ui/elementinfowidget.h | 2 + sources/ui/multielementinfowidget.cpp | 21 +++- tests/qttest/tst_terminallabeldisplay.cpp | 120 ++++++++++++++++++++++ 6 files changed, 238 insertions(+), 4 deletions(-) diff --git a/sources/qetgraphicsitem/element.cpp b/sources/qetgraphicsitem/element.cpp index 4c22353d4..c978035a7 100644 --- a/sources/qetgraphicsitem/element.cpp +++ b/sources/qetgraphicsitem/element.cpp @@ -2769,7 +2769,28 @@ QString Element::actualLabel() QString Element::labelForDisplay() { const QString label = actualLabel(); - if (!QetSettings::terminalLabelSplit()) { + const bool split = QetSettings::terminalLabelSplit(); + + //The "Show name" box of the selection properties hides the name + //of this element on the sheet only: the element keeps all of its + //data, and a terminal keeps its number, drawn on its own while + //the labelling is split. + if (!labelShown()) + { + if (!split) { + return QString(); + } + //The number alone in the label field, but only while the + //symbol has no field of its own for it: the number is + //drawn once, wherever it is drawn at all. + const int hidden_colon = label.lastIndexOf(QLatin1Char(':')); + if (hidden_colon < 0 || hasTerminalNumberText()) { + return QString(); + } + return label.mid(hidden_colon + 1); + } + + if (!split) { return label; } @@ -2837,6 +2858,29 @@ bool Element::hasTerminalNumberText() const return false; } +/** + @brief Element::labelShown + @return whether the label of this element must be drawn on the sheet. + The choice belongs to the element itself, set with the "Show name" + box of the selection properties: an element whose name is not shown + keeps all of its data, nothing is only not drawn. An element which + never got the box unticked keeps drawing its name, which is why a + missing information means true. +*/ +bool Element::labelShown() const +{ + //Only a terminal may have its name undrawn: an information + //which says otherwise, from whatever source, is not obeyed. + if (m_data.m_type != ElementData::Terminal) { + return true; + } + if (!m_data.m_informations.contains(QStringLiteral("label_shown"))) { + return true; + } + return QET::infoFlagIsTrue( + m_data.m_informations.value(QStringLiteral("label_shown")).toString()); +} + /** @brief Element::infoForDisplay @return what a text field bound to @p info_name must show: the label diff --git a/sources/qetgraphicsitem/element.h b/sources/qetgraphicsitem/element.h index 65dc5ce6f..a7d5b2ef1 100644 --- a/sources/qetgraphicsitem/element.h +++ b/sources/qetgraphicsitem/element.h @@ -175,6 +175,7 @@ class Element : public QetGraphicsItem QString terminalNumberForDisplay(); QString infoForDisplay(const QString &info_name); bool hasTerminalNumberText() const; + bool labelShown() const; /** The fields which draw the label draw it again: called when what they show of the label has changed under them, a diff --git a/sources/ui/elementinfowidget.cpp b/sources/ui/elementinfowidget.cpp index cd1640bb9..0f204331a 100644 --- a/sources/ui/elementinfowidget.cpp +++ b/sources/ui/elementinfowidget.cpp @@ -262,6 +262,9 @@ void ElementInfoWidget::enableLiveEdit() if (m_freeze_cb) { connect(m_freeze_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply); } + if (m_label_shown_cb) { + connect(m_label_shown_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply); + } if (m_number_cb) { connect(m_number_cb, QOverload::of(&QComboBox::activated), this, &ElementInfoWidget::apply); } @@ -283,6 +286,9 @@ void ElementInfoWidget::disableLiveEdit() if (m_freeze_cb) { disconnect(m_freeze_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply); } + if (m_label_shown_cb) { + disconnect(m_label_shown_cb, &QCheckBox::clicked, this, &ElementInfoWidget::apply); + } if (m_number_cb) { disconnect(m_number_cb, QOverload::of(&QComboBox::activated), this, &ElementInfoWidget::apply); } @@ -318,6 +324,7 @@ void ElementInfoWidget::buildInterface() setupSchemeRow(); setupNumberRow(); setupFreezeRow(); + setupLabelShownRow(); m_add_custom_property_btn = new QPushButton(tr("Add a custom property"), this); connect(m_add_custom_property_btn, &QPushButton::clicked, this, [this]() { addCustomProperty(); }); @@ -367,7 +374,8 @@ QStringList ElementInfoWidget::predefinedKeys() const keys << QETInformation::ELMT_FORMULA_ID << QStringLiteral("auto_num_locked") << QStringLiteral("potential_isolating") - << QStringLiteral("exclude_from_bom"); + << QStringLiteral("exclude_from_bom") + << QStringLiteral("label_shown"); return keys; } @@ -672,6 +680,9 @@ void ElementInfoWidget::updateUi() m_freeze_cb->setChecked(m_element->isFreezeLabel()); updateFreezeRow(); } + if (m_label_shown_cb) { + m_label_shown_cb->setChecked(m_element->labelShown()); + } refreshNumberRow(); updateSuggestions(); @@ -759,6 +770,13 @@ DiagramContext ElementInfoWidget::currentInfo() const info_.addValue(QStringLiteral("exclude_from_bom"), m_exclude_from_bom_cb->isChecked() ? QStringLiteral("true") : QStringLiteral("false")); } + // The name is drawn unless the element says otherwise: only a name + // which is not drawn needs an information, so that elements which + // draw it keep their files exactly as they are. + if (m_label_shown_cb && !m_label_shown_cb->isChecked()) { + info_.addValue(QStringLiteral("label_shown"), QStringLiteral("false")); + } + //The element keeps following its numbering scheme as long as its //formula is left as it is; a formula edited by hand is its own. const DiagramContext &elmt_info = m_element->elementInformations(); @@ -846,6 +864,38 @@ void ElementInfoWidget::updateFreezeRow() updateNumberRow(); } +/** + @brief ElementInfoWidget::setupLabelShownRow + Add under the freeze box a check box which decides whether the name + of this element is drawn on the sheet: the choice belongs to the + placed element, not to the symbol, so everyone can decide for + himself. +*/ +void ElementInfoWidget::setupLabelShownRow() +{ + //The box means something for a terminal only, so it is drawn + //for a terminal only - the same rule as the function separation + //box just below it. + if (!m_element || m_element->elementData().m_type != ElementData::Terminal) { + return; + } + m_label_shown_cb = new QCheckBox(tr("Show name"), this); + m_label_shown_cb->setObjectName(QStringLiteral("m_label_shown_cb")); + m_label_shown_cb->setToolTip(tr("Untick to not draw the name of this element on the sheet: " + "its data is kept, and a terminal keeps its number.")); + + int row = ui->scroll_vlayout->indexOf(m_freeze_cb); + if (row < 0) { + ElementInfoPartWidget *label = infoPartWidgetForKey(QETInformation::ELMT_LABEL); + row = label ? ui->scroll_vlayout->indexOf(label) : -1; + } + if (row >= 0) { + ui->scroll_vlayout->insertWidget(row + 1, m_label_shown_cb); + } else { + ui->scroll_vlayout->addWidget(m_label_shown_cb); + } +} + /** @brief ElementInfoWidget::setupNumberRow Add under the numbering a list of the numbers the element may be given diff --git a/sources/ui/elementinfowidget.h b/sources/ui/elementinfowidget.h index 2271becf8..f51924d89 100644 --- a/sources/ui/elementinfowidget.h +++ b/sources/ui/elementinfowidget.h @@ -76,6 +76,7 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget void updateSuggestions(); void setupSchemeRow(); void setupFreezeRow(); + void setupLabelShownRow(); void setupNumberRow(); void refreshNumberRow(); void updateNumberRow(); @@ -103,6 +104,7 @@ class ElementInfoWidget : public AbstractElementPropertiesEditorWidget QCheckBox *m_exclude_from_bom_cb = nullptr; QComboBox *m_scheme_cb = nullptr; QCheckBox *m_freeze_cb = nullptr; + QCheckBox *m_label_shown_cb = nullptr; QWidget *m_number_row = nullptr; QComboBox *m_number_cb = nullptr; int m_number_current = -1; ///< the number the element has, -1 if unknown diff --git a/sources/ui/multielementinfowidget.cpp b/sources/ui/multielementinfowidget.cpp index be226cbae..49d31b56a 100644 --- a/sources/ui/multielementinfowidget.cpp +++ b/sources/ui/multielementinfowidget.cpp @@ -75,6 +75,15 @@ quint64 s_last_session = 0; const QString EXCLUDE_FROM_BOM = QStringLiteral("exclude_from_bom"); const QString AUTO_NUM_LOCKED = QStringLiteral("auto_num_locked"); const QString POTENTIAL_ISOLATING = QStringLiteral("potential_isolating"); + //The name is drawn unless the element says otherwise (@sa + //Element::labelShown): an element which never got the box unticked + //has no information for it, and counts as "shown". +const QString LABEL_SHOWN = QStringLiteral("label_shown"); + +bool flagDefaultIsOn(const QString &key) +{ + return key == LABEL_SHOWN; +} QString flagText(bool on) { @@ -449,7 +458,8 @@ void MultiElementInfoWidget::buildInterface() const QList> flags { {EXCLUDE_FROM_BOM, tr("Exclude from the bill of materials")}, {POTENTIAL_ISOLATING, tr("Function separation")}, - {AUTO_NUM_LOCKED, tr("Exclude from automatic numbering")} + {AUTO_NUM_LOCKED, tr("Exclude from automatic numbering")}, + {LABEL_SHOWN, tr("Show name")} }; for (const auto &pair : flags) { @@ -588,12 +598,19 @@ void MultiElementInfoWidget::fillRows() for (Flag *flag : std::as_const(m_flags)) { + //"Show name" means something for a terminal only, so it is + //shown for terminals only - the same rule as the two flags + //before it. flag->check->setVisible(flag->key == EXCLUDE_FROM_BOM || all_terminals); flag->restore->setVisible(flag->check->isVisible()); int on = 0; for (Element *element : list) { - if (QET::infoFlagIsTrue(m_snapshot.value(element).value(flag->key).toString())) { + const QVariant value = m_snapshot.value(element).value(flag->key); + const bool is_on = value.isValid() + ? QET::infoFlagIsTrue(value.toString()) + : flagDefaultIsOn(flag->key); + if (is_on) { ++on; } } diff --git a/tests/qttest/tst_terminallabeldisplay.cpp b/tests/qttest/tst_terminallabeldisplay.cpp index b3f591ec2..e52ecc89c 100644 --- a/tests/qttest/tst_terminallabeldisplay.cpp +++ b/tests/qttest/tst_terminallabeldisplay.cpp @@ -19,6 +19,8 @@ #include "qetapptest.h" #include "TerminalStrip/realterminal.h" #include "TerminalStrip/terminalstrip.h" +#include "diagram.h" +#include "properties/elementdata.h" #include "qetgraphicsitem/dynamicelementtextitem.h" #include "qetgraphicsitem/element.h" #include "qetgraphicsitem/terminal.h" @@ -85,6 +87,28 @@ class tst_terminallabeldisplay : public QObject return number; } + //The "Show name" box of the selection properties, unticked: the + //element then stores the information "label_shown" = "false", the + //only thing it stores for it + static void hideName(Element *element) + { + DiagramContext dc = element->elementInformations(); + dc.addValue(QStringLiteral("label_shown"), QStringLiteral("false")); + element->setElementInformations(dc); + } + + //Any element of the fixture which is not a terminal and carries a + //name to draw + static Element *aNonTerminal(QETProject &project) + { + for (Diagram *diagram : project.diagrams()) + for (Element *element : diagram->elements()) + if (element->elementData().m_type != ElementData::Terminal + && !element->actualLabel().isEmpty()) + return element; + return nullptr; + } + private slots: void cleanup() { @@ -215,6 +239,102 @@ private slots: QCOMPARE(label->toPlainText(), QStringLiteral("X1:1")); QCOMPARE(element->actualLabel(), QStringLiteral("X1:1")); } + + //A terminal whose name must not be drawn draws nothing of it, but + //keeps everything: the stored label is still there, and so is every + //use of it as data -- only the drawing changes + void hiddenNameKeepsEverythingStored() + { + QetSettings::setTerminalLabelSplit(false); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = terminalWithLabel(project, QStringLiteral("X1:1")); + QVERIFY(element); + hideName(element); + + QVERIFY(!element->labelShown()); + QCOMPARE(element->actualLabel(), QStringLiteral("X1:1")); + QCOMPARE(element->labelForDisplay(), QString()); + + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY(label); + QCOMPARE(label->toPlainText(), QString()); + } + + //Hidden, and the labelling is split: the terminal keeps its number, + //drawn on its own line while the symbol has no field for it, so + //hiding the name can never hide the number + void hiddenNameKeepsTheNumberWithSplit() + { + QetSettings::setTerminalLabelSplit(true); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = terminalWithLabel(project, QStringLiteral("X1:1")); + QVERIFY(element); + hideName(element); + + QCOMPARE(element->labelForDisplay(), QStringLiteral("1")); + QCOMPARE(element->terminalNumberForDisplay(), QStringLiteral("1")); + QCOMPARE(element->actualLabel(), QStringLiteral("X1:1")); + + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY(label); + QCOMPARE(label->toPlainText(), QStringLiteral("1")); + } + + //Hidden, split, and the symbol has a field of its own for the + //number: the number is drawn in its own field, the label field + //draws nothing -- the number is never drawn twice + void hiddenNameLeavesTheNumberToItsOwnField() + { + QetSettings::setTerminalLabelSplit(true); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = terminalWithLabel(project, QStringLiteral("X1:1")); + QVERIFY(element); + DynamicElementTextItem *number = addNumberField(element); + hideName(element); + + QCOMPARE(element->labelForDisplay(), QString()); + DynamicElementTextItem *label = fieldBoundTo(element, QETInformation::ELMT_LABEL); + QVERIFY(label); + QCOMPARE(label->toPlainText(), QString()); + QCOMPARE(number->toPlainText(), QStringLiteral("1")); + } + + //Only a terminal may have its name undrawn: another element which + //carries the same information still draws its name, whatever the + //file says + void nonTerminalAlwaysDrawsItsName() + { + QetSettings::setTerminalLabelSplit(false); + + const QString fixture = QFINDTESTDATA("fixtures/every_feature.qet"); + QETProject project(fixture); + QCOMPARE(project.state(), QETProject::Ok); + + Element *element = aNonTerminal(project); + QVERIFY2(element, "the fixture has an element which is no terminal"); + DiagramContext dc = element->elementInformations(); + dc.addValue(QStringLiteral("label_shown"), QStringLiteral("false")); + element->setElementInformations(dc); + + //Whatever the file says, a non-terminal draws its name: + //the information is not obeyed, and the drawn name is the + //stored one, composed as it always is + QVERIFY(element->labelShown()); + QVERIFY(!element->actualLabel().isEmpty()); + QCOMPARE(element->labelForDisplay(), element->actualLabel()); + } }; QET_APP_TEST_MAIN(tst_terminallabeldisplay)