diff --git a/cmake/qet_compilation_vars.cmake b/cmake/qet_compilation_vars.cmake index efe2f1992..833a125da 100644 --- a/cmake/qet_compilation_vars.cmake +++ b/cmake/qet_compilation_vars.cmake @@ -242,6 +242,8 @@ set(QET_SRC_FILES ${QET_DIR}/sources/genericpanel.h ${QET_DIR}/sources/itemgroups.cpp ${QET_DIR}/sources/itemgroups.h + ${QET_DIR}/sources/shownkinds.cpp + ${QET_DIR}/sources/shownkinds.h ${QET_DIR}/sources/lastusedstyle.cpp ${QET_DIR}/sources/lastusedstyle.h ${QET_DIR}/sources/machine_info.cpp diff --git a/sources/dxfexport.cpp b/sources/dxfexport.cpp index 182634f38..5f3c266ff 100644 --- a/sources/dxfexport.cpp +++ b/sources/dxfexport.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "dxfexport.h" +#include "shownkinds.h" #include "conductorsegment.h" #include "createdxf.h" @@ -135,6 +136,10 @@ void DxfExport::write(Diagram *diagram, int width, int height, } } for (QGraphicsItem *qgi : std::as_const(stacked_items)) { + //Left out like on screen and in print (View > Show, #301) + if (ShownKinds::isHidden(qgi)) { + continue; + } if (Element *elmt = qgraphicsitem_cast(qgi)) { list_elements << elmt; } else if (Conductor *f = qgraphicsitem_cast(qgi)) { @@ -147,7 +152,8 @@ void DxfExport::write(Diagram *diagram, int width, int height, list_shapes << dii; } else if (DynamicElementTextItem *deti = qgraphicsitem_cast(qgi)) { list_texts << deti; - if (QGraphicsTextItem *xref = deti->slaveXrefItem()) { + QGraphicsTextItem *xref = deti->slaveXrefItem(); + if (xref && !ShownKinds::isHidden(xref)) { list_xref_texts << xref; } } else if (QetGraphicsTableItem *gti = qgraphicsitem_cast(qgi)) { @@ -290,7 +296,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, Createdxf::layer = Layer::WireNumbers; ConductorTextItem *textItem = cond -> textItem(); - if (textItem) { + if (textItem && !ShownKinds::isHidden(textItem)) { qreal fontSize = textItem -> font().pointSizeF(); if (fontSize < 0) fontSize = textItem -> font().pixelSize(); diff --git a/sources/qetapp.h b/sources/qetapp.h index 309002808..cd58aa671 100644 --- a/sources/qetapp.h +++ b/sources/qetapp.h @@ -264,6 +264,8 @@ class QETApp : public QObject signals: /// The text grid setting changed, see TextGrid. void textGridChanged(); + /// A kind of item was shown or hidden, see ShownKinds. + void shownKindsChanged(); public slots: void systray(QSystemTrayIcon::ActivationReason); diff --git a/sources/qetdiagrameditor.cpp b/sources/qetdiagrameditor.cpp index 86b2e6c3d..41c8e82e5 100644 --- a/sources/qetdiagrameditor.cpp +++ b/sources/qetdiagrameditor.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "qetdiagrameditor.h" +#include "shownkinds.h" #ifdef QET_HAS_SCRIPTING #include "scripting/qetscripting.h" #include "scripting/scriptlibrary.h" @@ -607,6 +608,36 @@ void QETDiagramEditor::setUpActions() dv->setCellLinesShown(checked); }); + //Show or hide whole kinds of items on every folio (bugtracker #301) + m_shown_kinds_menu = new QMenu(tr("Afficher"), this); + const QList> kinds { + {ShownKinds::SymbolTexts, tr("Textes des éléments")}, + {ShownKinds::WireNumbers, tr("Textes des conducteurs")}, + {ShownKinds::FreeTexts, tr("Champs de texte")}, + {ShownKinds::Shapes, tr("Formes")}, + {ShownKinds::Pictures, tr("Images")}, + {ShownKinds::Tables, tr("Tableaux")}, + {ShownKinds::CrossReferences, tr("Références croisées")}}; + for (const auto &kind : kinds) + { + QAction *action = m_shown_kinds_menu->addAction(kind.second); + action->setCheckable(true); + action->setData(int(kind.first)); + connect(action, &QAction::triggered, this, [kind](bool checked) { + ShownKinds::setShown(kind.first, checked); + for (QETProject *project : QETApp::registeredProjects()) + for (Diagram *diagram : project->diagrams()) + ShownKinds::apply(diagram, kind.first); + emit QETApp::instance()->shownKindsChanged(); + }); + } + m_hidden_kinds_label = new QLabel(this); + m_hidden_kinds_label->setToolTip(tr("Voir Affichage > Afficher")); + statusBar()->addPermanentWidget(m_hidden_kinds_label); + connect(QETApp::instance(), &QETApp::shownKindsChanged, + this, &QETDiagramEditor::updateShownKinds); + updateShownKinds(); + //Edit current diagram properties m_edit_diagram_properties = new QAction(QET::Icons::DialogInformation, tr("Propriétés du folio"), this); ShortcutManager::instance().registerAction(m_edit_diagram_properties, "diagrameditor.edit_diagram_properties", tr("Éditeur de schémas"), Qt::CTRL | Qt::Key_L); @@ -1458,6 +1489,7 @@ void QETDiagramEditor::setUpMenu() menu_affichage -> addAction(m_draw_guides); menu_affichage -> addAction(m_cell_rulers); menu_affichage -> addAction(m_cell_lines); + menu_affichage -> addMenu(m_shown_kinds_menu); menu_affichage -> addMenu(m_background_color_button->menu()); menu_affichage -> addSeparator(); menu_affichage -> addActions(m_zoom_actions_group.actions()); @@ -4140,6 +4172,21 @@ void QETDiagramEditor::updateScriptActions() } #endif +/** + @brief QETDiagramEditor::updateShownKinds + Tick the kinds shown in View > Show, and say in the status bar how many + are hidden, so that a hidden kind is never forgotten. +*/ +void QETDiagramEditor::updateShownKinds() +{ + for (QAction *action : m_shown_kinds_menu->actions()) + action->setChecked(ShownKinds::isShown(ShownKinds::Kind(action->data().toInt()))); + + const int hidden = ShownKinds::hiddenCount(); + m_hidden_kinds_label->setText(tr("%n type(s) d'objets masqué(s)", "", hidden)); + m_hidden_kinds_label->setVisible(hidden > 0); +} + /** @brief QETDiagramEditor::updateTextGridButton Show the current text grid on its toolbar button and check it in its menu. diff --git a/sources/qetdiagrameditor.h b/sources/qetdiagrameditor.h index f585e3d8f..500a90e91 100644 --- a/sources/qetdiagrameditor.h +++ b/sources/qetdiagrameditor.h @@ -30,6 +30,7 @@ #include class QToolButton; +class QLabel; class QMdiSubWindow; class QETProject; @@ -187,6 +188,7 @@ class QETDiagramEditor : public QETMainWindow private slots: void updateTextGridButton(); + void updateShownKinds(); void selectionChanged(); public: @@ -284,6 +286,8 @@ class QETDiagramEditor : public QETMainWindow DiagramBgColorToolButton *m_background_color_button = nullptr; QMenu *m_text_grid_menu = nullptr; ///< Snap step used when dragging texts QToolButton *m_text_grid_button = nullptr; + QMenu *m_shown_kinds_menu = nullptr; ///< View > Show, kinds of items (#301) + QLabel *m_hidden_kinds_label = nullptr; ///< Status bar: how many kinds are hidden QList m_zoom_action_toolBar; ///Only zoom action must displayed in the toolbar diff --git a/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp b/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp index fd9bbe6ba..66e7f8bc5 100644 --- a/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp +++ b/sources/qetgraphicsitem/ViewItem/qetgraphicstableitem.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "qetgraphicstableitem.h" +#include "../../shownkinds.h" #include "../../qetproject.h" #include "../../QPropertyUndoCommand/qpropertyundocommand.h" #include "../../createdxf.h" @@ -133,6 +134,7 @@ void QetGraphicsTableItem::checkInsufficientRowsCount( QetGraphicsTableItem::QetGraphicsTableItem(QGraphicsItem *parent) : QetGraphicsItem(parent) { + ShownKinds::tag(this, ShownKinds::Tables); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); setAcceptHoverEvents(true); diff --git a/sources/qetgraphicsitem/conductor.cpp b/sources/qetgraphicsitem/conductor.cpp index 95b298442..aab4465d3 100644 --- a/sources/qetgraphicsitem/conductor.cpp +++ b/sources/qetgraphicsitem/conductor.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "../qetgraphicsitem/conductor.h" +#include "../shownkinds.h" #include "../lastusedstyle.h" #include "../qetproject.h" #include "../QPropertyUndoCommand/qpropertyundocommand.h" @@ -1521,6 +1522,20 @@ QPointF Conductor::posForText(Qt::Orientations &flag) return p1; } +/** + @brief Conductor::updateTextVisibility + Show the text of this conductor if its properties ask for it and wire + numbers are not hidden (View > Show). With "one text per potential", + call calculateTextItemPosition() afterwards: it hides all texts of the + potential but the longest conductor's. +*/ +void Conductor::updateTextVisibility() +{ + ShownKinds::setVisible(m_text_item, + m_properties.type == ConductorProperties::Multi + && m_properties.m_show_text); +} + /** @brief Conductor::calculateTextItemPosition Move the text at middle of conductor (if is vertical or horizontal) @@ -1548,10 +1563,11 @@ void Conductor::calculateTextItemPosition() //At this point this conductor is the longest conductor we hide all text of conductor_list foreach (Conductor *c, relatedPotentialConductors(false)) { - c -> textItem() -> setVisible(false); + ShownKinds::setVisible(c -> textItem(), false); } - //Make sure text item is visible - m_text_item -> setVisible(true); + //Make sure text item is visible, unless wire numbers are hidden + //(View > Show) + ShownKinds::setVisible(m_text_item, true); } //position @@ -1806,11 +1822,7 @@ void Conductor::setProperties(const ConductorProperties &property) m_text_item->setFont(font); m_text_item->setColor(m_properties.text_color); - if (m_properties.type != ConductorProperties::Multi) - m_text_item->setVisible(false); - else - m_text_item->setVisible(m_properties.m_show_text); - + updateTextVisibility(); calculateTextItemPosition(); update(); diff --git a/sources/qetgraphicsitem/conductor.h b/sources/qetgraphicsitem/conductor.h index 2cbe76f27..feff48004 100644 --- a/sources/qetgraphicsitem/conductor.h +++ b/sources/qetgraphicsitem/conductor.h @@ -129,6 +129,7 @@ class Conductor : public QGraphicsObject void setProfiles(const ConductorProfilesGroup &); ConductorProfilesGroup profiles() const; void calculateTextItemPosition(); + void updateTextVisibility(); virtual Highlight highlight() const; virtual void setHighlighted(Highlight); QSet relatedPotentialConductors( diff --git a/sources/qetgraphicsitem/conductortextitem.cpp b/sources/qetgraphicsitem/conductortextitem.cpp index a3e3127f0..c5ee20845 100644 --- a/sources/qetgraphicsitem/conductortextitem.cpp +++ b/sources/qetgraphicsitem/conductortextitem.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "conductortextitem.h" +#include "../shownkinds.h" #include "../diagram.h" #include "../diagramcommands.h" @@ -31,6 +32,7 @@ ConductorTextItem::ConductorTextItem(Conductor *parent_conductor) : moved_by_user_(false), rotate_by_user_(false) { + ShownKinds::tag(this, ShownKinds::WireNumbers); setAcceptHoverEvents(true); } @@ -45,7 +47,9 @@ ConductorTextItem::ConductorTextItem( parent_conductor_(parent_conductor), moved_by_user_(false), rotate_by_user_(false) -{} +{ + ShownKinds::tag(this, ShownKinds::WireNumbers); +} /** Destructeur diff --git a/sources/qetgraphicsitem/crossrefitem.cpp b/sources/qetgraphicsitem/crossrefitem.cpp index 60b0f7ca9..fd9267938 100644 --- a/sources/qetgraphicsitem/crossrefitem.cpp +++ b/sources/qetgraphicsitem/crossrefitem.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "crossrefitem.h" +#include "../shownkinds.h" #include #include "../qetproject.h" @@ -81,6 +82,7 @@ CrossRefItem::~CrossRefItem() */ void CrossRefItem::init() { + ShownKinds::tag(this, ShownKinds::CrossReferences); if(!m_element->diagram()) { qDebug() << "CrossRefItem constructor" << "element is not in a diagram"; @@ -238,11 +240,13 @@ void CrossRefItem::updateProperties() if (m_properties != xrp) { m_properties = xrp; - hide(); + //Through ShownKinds: stays hidden while View > Show hides + //cross-references + ShownKinds::setVisible(this, false); if(m_properties.snapTo() == XRefProperties::Label && (m_text || m_group)) //Snap to label and parent is text or group - show(); + ShownKinds::setVisible(this, true); else if((m_properties.snapTo() == XRefProperties::Bottom && !m_text && !m_group)) //Snap to bottom of element is the parent - show(); + ShownKinds::setVisible(this, true); setUpConnection(); updateLabel(); @@ -528,7 +532,9 @@ void CrossRefItem::linkedChanged() m_slave_connection.clear(); - if(!isVisible()) + //Hidden only by View > Show: keep following the slaves, so the + //label is right when it is shown again + if(!isVisible() && !ShownKinds::isHidden(this)) return; for(Element *elmt : m_element->linkedElements()) diff --git a/sources/qetgraphicsitem/diagramimageitem.cpp b/sources/qetgraphicsitem/diagramimageitem.cpp index ade3d987c..ac82aa5bd 100644 --- a/sources/qetgraphicsitem/diagramimageitem.cpp +++ b/sources/qetgraphicsitem/diagramimageitem.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "diagramimageitem.h" +#include "../shownkinds.h" #include "../PropertiesEditor/propertieseditordialog.h" #include "../QPropertyUndoCommand/qpropertyundocommand.h" @@ -50,6 +51,7 @@ DiagramImageItem::DiagramImageItem(QetGraphicsItem *parent_item): QetGraphicsItem(parent_item) { + ShownKinds::tag(this, ShownKinds::Pictures); setFlags(QGraphicsItem::ItemIsSelectable|QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemSendsGeometryChanges); setAcceptHoverEvents(true); } @@ -66,6 +68,7 @@ DiagramImageItem::DiagramImageItem(const QPixmap &pixmap, QetGraphicsItem *paren m_base_pixmap(pixmap), m_crop_rect(pixmap.rect()) { + ShownKinds::tag(this, ShownKinds::Pictures); // m_transform.toMatrix(), not QGraphicsItem::setRotation()/setScale(): // those are a single uniform scale() float, which is exactly why an // image could never break its own aspect ratio before this class diff --git a/sources/qetgraphicsitem/dynamicelementtextitem.cpp b/sources/qetgraphicsitem/dynamicelementtextitem.cpp index 515aa2d8b..51e3756c6 100644 --- a/sources/qetgraphicsitem/dynamicelementtextitem.cpp +++ b/sources/qetgraphicsitem/dynamicelementtextitem.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "dynamicelementtextitem.h" +#include "../shownkinds.h" #include "../qetproject.h" #include "../QPropertyUndoCommand/qpropertyundocommand.h" #include "../diagram.h" @@ -44,6 +45,7 @@ DynamicElementTextItem::DynamicElementTextItem(Element *parent_element) : m_parent_element(parent_element), m_uuid(QUuid::createUuid()) { + ShownKinds::tag(this, ShownKinds::SymbolTexts); setFont(QETApp::dynamicTextsItemFont()); setText(tr("Texte")); setParentItem(parent_element); @@ -1662,6 +1664,7 @@ void DynamicElementTextItem::updateXref() if(!m_slave_Xref_item) { m_slave_Xref_item = new QGraphicsTextItem(xref_label, this); + ShownKinds::tag(m_slave_Xref_item, ShownKinds::CrossReferences); m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5)); // Match the parent text's user-configurable color instead of // hardcoding black, which renders invisible under dark themes diff --git a/sources/qetgraphicsitem/elementtextitemgroup.cpp b/sources/qetgraphicsitem/elementtextitemgroup.cpp index 9e4016fcf..8c89e7c50 100644 --- a/sources/qetgraphicsitem/elementtextitemgroup.cpp +++ b/sources/qetgraphicsitem/elementtextitemgroup.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "elementtextitemgroup.h" +#include "../shownkinds.h" #include "../qetproject.h" #include "../QPropertyUndoCommand/qpropertyundocommand.h" #include "../diagram.h" @@ -53,6 +54,7 @@ ElementTextItemGroup::ElementTextItemGroup(const QString &name, m_name(name), m_parent_element(parent) { + ShownKinds::tag(this, ShownKinds::SymbolTexts); setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); connect(parent, @@ -882,6 +884,7 @@ void ElementTextItemGroup::updateXref() if(!m_slave_Xref_item) { m_slave_Xref_item = new QGraphicsTextItem(xref_label, this); + ShownKinds::tag(m_slave_Xref_item, ShownKinds::CrossReferences); m_slave_Xref_item->setFont(QETApp::diagramTextsFont(5)); m_update_slave_Xref_connection << connect(master_elmt, &Element::xChanged, this, &ElementTextItemGroup::updateXref); diff --git a/sources/qetgraphicsitem/independenttextitem.cpp b/sources/qetgraphicsitem/independenttextitem.cpp index 03e0ba2bb..bb7af5fa8 100644 --- a/sources/qetgraphicsitem/independenttextitem.cpp +++ b/sources/qetgraphicsitem/independenttextitem.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "independenttextitem.h" +#include "../shownkinds.h" #include "../diagram.h" #include "../diagramcommands.h" @@ -34,6 +35,7 @@ IndependentTextItem::IndependentTextItem() : DiagramTextItem(nullptr) { + ShownKinds::tag(this, ShownKinds::FreeTexts); //Start from the font last applied to a text item this session, //falling back to the app-wide Preferences default otherwise. setFont(LastUsedStyle::hasTextFont() ? LastUsedStyle::textFont() @@ -49,7 +51,9 @@ IndependentTextItem::IndependentTextItem() : */ IndependentTextItem::IndependentTextItem(const QString &text) : DiagramTextItem(text, nullptr) -{} +{ + ShownKinds::tag(this, ShownKinds::FreeTexts); +} /// Destructeur IndependentTextItem::~IndependentTextItem() diff --git a/sources/qetgraphicsitem/qetshapeitem.cpp b/sources/qetgraphicsitem/qetshapeitem.cpp index 8c1be55e4..43e918ca1 100644 --- a/sources/qetgraphicsitem/qetshapeitem.cpp +++ b/sources/qetgraphicsitem/qetshapeitem.cpp @@ -16,6 +16,7 @@ along with QElectroTech. If not, see . */ #include "qetshapeitem.h" +#include "../shownkinds.h" #include "../PropertiesEditor/propertieseditordialog.h" #include "../QPropertyUndoCommand/qpropertyundocommand.h" @@ -57,6 +58,7 @@ QetShapeItem::QetShapeItem(QPointF p1, QPointF p2, ShapeType type, QGraphicsItem m_P2 (p2), m_hovered(false) { + ShownKinds::tag(this, ShownKinds::Shapes); if (type == Polygon) m_polygon << m_P1 << m_P2; setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges); setAcceptHoverEvents(true); diff --git a/sources/scripting/qetscriptapi.cpp b/sources/scripting/qetscriptapi.cpp index d61768793..a72d408cb 100644 --- a/sources/scripting/qetscriptapi.cpp +++ b/sources/scripting/qetscriptapi.cpp @@ -4098,10 +4098,7 @@ bool QetScriptApi::setConductorDefault(int folioIndex, const QString &property, { const QList conductor_list = diagram->conductors(); for (Conductor *c : conductor_list) - { - const ConductorProperties cp = c->properties(); - c->textItem()->setVisible(cp.type == ConductorProperties::Multi && cp.m_show_text); - } + c->updateTextVisibility(); for (Conductor *c : conductor_list) c->calculateTextItemPosition(); } diff --git a/sources/shownkinds.cpp b/sources/shownkinds.cpp new file mode 100644 index 000000000..b8afae7b3 --- /dev/null +++ b/sources/shownkinds.cpp @@ -0,0 +1,149 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include "shownkinds.h" + +#include +#include + +namespace +{ + bool hidden[ShownKinds::KindCount] = {}; +} + +/** + @return true unless @a kind was hidden with setShown(). +*/ +bool ShownKinds::isShown(Kind kind) +{ + return kind >= KindCount || !hidden[kind]; +} + +/** + Show or hide @a kind for the items created from now on. Items already + on a scene change when apply() is called on it. +*/ +void ShownKinds::setShown(Kind kind, bool shown) +{ + if (kind < KindCount) { + hidden[kind] = !shown; + } +} + +/** + @return how many kinds are hidden, for the status bar. +*/ +int ShownKinds::hiddenCount() +{ + int count = 0; + for (bool h : hidden) { + count += h ? 1 : 0; + } + return count; +} + +/** + Mark @a item as being of @a kind, and hide it now if that kind is + hidden. Called by the item's constructor, while the item still wants to + be visible. +*/ +void ShownKinds::tag(QGraphicsItem *item, Kind kind) +{ + if (!item) { + return; + } + item->setData(data_key, int(kind)); + if (!isShown(kind)) { + item->setVisible(false); + item->setData(hidden_key, true); + } +} + +/** + Show or hide @a item as its own code wants, unless its kind is hidden: + then it stays hidden, and comes back when the kind is shown again. + Use this instead of QGraphicsItem::setVisible() in a tagged item. +*/ +void ShownKinds::setVisible(QGraphicsItem *item, bool visible) +{ + if (!item) { + return; + } + const QVariant kind = item->data(data_key); + const bool vetoed = visible && kind.isValid() && !isShown(Kind(kind.toInt())); + item->setVisible(visible && !vetoed); + item->setData(hidden_key, vetoed ? QVariant(true) : QVariant()); +} + +/** + @return true if @a item, or an item it hangs from, is of a hidden kind. + For code that walks the items itself (DXF export) and must leave out + what View > Show hides. +*/ +bool ShownKinds::isHidden(const QGraphicsItem *item) +{ + for (; item; item = item->parentItem()) + { + const QVariant value = item->data(data_key); + if (value.isValid() && !isShown(Kind(value.toInt()))) { + return true; + } + } + return false; +} + +/** + @return true if @a item is visible, or would be but for its kind being + hidden. For code that asks isVisible() to learn what an item's own code + decided (which wire carries the text of a potential). +*/ +bool ShownKinds::wantsVisible(const QGraphicsItem *item) +{ + return item && (item->isVisible() || item->data(hidden_key).toBool()); +} + +/** + Bring the items of @a kind on @a scene in line with the current state: + hide the ones that are visible, or show again the ones hidden for it. +*/ +void ShownKinds::apply(QGraphicsScene *scene, Kind kind) +{ + if (!scene) { + return; + } + const bool shown = isShown(kind); + //items() with no argument also returns the hidden items + const QList items = scene->items(); + for (QGraphicsItem *item : items) + { + const QVariant value = item->data(data_key); + if (!value.isValid() || value.toInt() != kind) { + continue; + } + if (!shown) { + //Its own flag, not isVisible(): a symbol text under a + //hidden group still wants to be visible + if (item->isVisibleTo(item->parentItem())) { + item->setVisible(false); + item->setData(hidden_key, true); + } + } else if (item->data(hidden_key).toBool()) { + item->setVisible(true); + item->setData(hidden_key, QVariant()); + } + } +} diff --git a/sources/shownkinds.h b/sources/shownkinds.h new file mode 100644 index 000000000..39d254c49 --- /dev/null +++ b/sources/shownkinds.h @@ -0,0 +1,77 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef SHOWNKINDS_H +#define SHOWNKINDS_H + +class QGraphicsItem; +class QGraphicsScene; + +/** + Show or hide whole kinds of folio items, View > Show (bugtracker #301): + symbol texts, wire numbers, free texts and so on. + + Each item says which kind it is with a tag set by its constructor, so an + item created while its kind is hidden starts hidden, whichever way it was + created (load, paste, a text added to a symbol, a new cross-reference). + A tag and not type(): the cross-reference under a contact's label is a + plain QGraphicsTextItem. + + Some items also hide themselves (a wire number switched off, one text + per potential, a cross-reference snapped elsewhere). They call + setVisible() here instead of QGraphicsItem::setVisible(), and the kind + state can only veto what they ask for: showing a kind again brings back + only what was hidden for it, never what an item hid itself. + + The state is for the whole application and is not saved: it is a way of + looking at the folios, like the grid. Hidden items are left out of + printing and exports, and Qt does not select them, so nothing hidden is + copied, moved or deleted by accident. + + This part knows nothing of Diagram, so it can be tested on a plain scene. +*/ +namespace ShownKinds +{ + enum Kind { + SymbolTexts, + WireNumbers, + FreeTexts, + Shapes, + Pictures, + Tables, + CrossReferences, + KindCount + }; + + /// QGraphicsItem::data() key holding an item's kind. + inline constexpr int data_key = 0x4b4e44; // "KND" + /// QGraphicsItem::data() key set while an item that wants to be + /// visible is hidden only because its kind is hidden. + inline constexpr int hidden_key = 0x4b4e48; // "KNH" + + bool isShown(Kind kind); + void setShown(Kind kind, bool shown); + int hiddenCount(); + + void tag(QGraphicsItem *item, Kind kind); + void setVisible(QGraphicsItem *item, bool visible); + bool isHidden(const QGraphicsItem *item); + bool wantsVisible(const QGraphicsItem *item); + void apply(QGraphicsScene *scene, Kind kind); +} + +#endif // SHOWNKINDS_H diff --git a/sources/ui/diagrampropertiesdialog.cpp b/sources/ui/diagrampropertiesdialog.cpp index 673da010f..cc3046b9c 100644 --- a/sources/ui/diagrampropertiesdialog.cpp +++ b/sources/ui/diagrampropertiesdialog.cpp @@ -124,10 +124,7 @@ DiagramPropertiesDialog::DiagramPropertiesDialog(Diagram *diagram, QWidget *pare { const QList conductor_list = diagram -> conductors(); for (Conductor *c : conductor_list) - { - const ConductorProperties cp = c -> properties(); - c -> textItem() -> setVisible(cp.type == ConductorProperties::Multi && cp.m_show_text); - } + c -> updateTextVisibility(); for (Conductor *c : conductor_list) c -> calculateTextItemPosition(); } diff --git a/sources/undocommand/deleteqgraphicsitemcommand.cpp b/sources/undocommand/deleteqgraphicsitemcommand.cpp index a2db0baf8..661e17608 100644 --- a/sources/undocommand/deleteqgraphicsitemcommand.cpp +++ b/sources/undocommand/deleteqgraphicsitemcommand.cpp @@ -26,6 +26,7 @@ #include "../qetgraphicsitem/dynamicelementtextitem.h" #include "../qetgraphicsitem/element.h" #include "../qetgraphicsitem/elementtextitemgroup.h" +#include "../shownkinds.h" #include "../qetgraphicsitem/terminal.h" #include "addelementtextcommand.h" #include "../TerminalStrip/realterminal.h" @@ -324,7 +325,8 @@ void DeleteQGraphicsItemCommand::redo() //current conductor is visible (that mean the conductor have the single displayed text) //We call adjustTextItemPosition to other conductor at the same potential to keep //a visible text on this potential. - if (m_diagram -> defaultConductorProperties.m_one_text_per_folio && c -> textItem() -> isVisible()) + //wantsVisible(): also while wire texts are hidden (View > Show) + if (m_diagram -> defaultConductorProperties.m_one_text_per_folio && ShownKinds::wantsVisible(c -> textItem())) { QList conductor_list; conductor_list << c -> relatedPotentialConductors(false).values(); diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index fb57677c5..ed1cd6859 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -139,6 +139,14 @@ add_test(NAME tst_itemgroups COMMAND tst_itemgroups) target_include_directories(tst_itemgroups PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_itemgroups PRIVATE Qt::Test Qt::Widgets Qt::Xml) +# shownkinds.cpp keeps View > Show (bugtracker #301) apart from the item +# classes, so it is tested here on a plain scene of rectangles. +add_executable(tst_shownkinds tst_shownkinds.cpp ${QET_DIR}/sources/shownkinds.cpp) +add_test(NAME tst_shownkinds COMMAND tst_shownkinds) +set_tests_properties(tst_shownkinds PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +target_include_directories(tst_shownkinds PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_shownkinds PRIVATE Qt::Test Qt::Widgets) + # textanchor.h is header-only: the point of a text chosen by its alignment, # shown as Position X/Y in the text properties (#1155). add_executable(tst_textanchor tst_textanchor.cpp) diff --git a/tests/qttest/tst_shownkinds.cpp b/tests/qttest/tst_shownkinds.cpp new file mode 100644 index 000000000..048e81a4e --- /dev/null +++ b/tests/qttest/tst_shownkinds.cpp @@ -0,0 +1,208 @@ +#include +#include +#include + +#include "shownkinds.h" + +class tst_shownkinds : public QObject +{ + Q_OBJECT + + QGraphicsScene *scene = nullptr; + + QGraphicsRectItem *add(QGraphicsItem *parent = nullptr) + { + auto item = new QGraphicsRectItem(0, 0, 10, 10, parent); + item->setFlag(QGraphicsItem::ItemIsSelectable); + if (!parent) { + scene->addItem(item); + } + return item; + } + +private slots: + void init() + { + scene = new QGraphicsScene; + for (int k = 0; k < ShownKinds::KindCount; ++k) { + ShownKinds::setShown(ShownKinds::Kind(k), true); + } + } + + void cleanup() + { + delete scene; + scene = nullptr; + } + + void hideAndShowAgain() + { + auto shape = add(); + auto text = add(); + ShownKinds::tag(shape, ShownKinds::Shapes); + ShownKinds::tag(text, ShownKinds::FreeTexts); + + ShownKinds::setShown(ShownKinds::Shapes, false); + ShownKinds::apply(scene, ShownKinds::Shapes); + QVERIFY(!shape->isVisible()); + QVERIFY(text->isVisible()); + QCOMPARE(ShownKinds::hiddenCount(), 1); + + ShownKinds::setShown(ShownKinds::Shapes, true); + ShownKinds::apply(scene, ShownKinds::Shapes); + QVERIFY(shape->isVisible()); + QCOMPARE(ShownKinds::hiddenCount(), 0); + } + + // Created while its kind is hidden: starts hidden, whatever created it. + void createdWhileHidden() + { + ShownKinds::setShown(ShownKinds::Pictures, false); + auto picture = add(); + ShownKinds::tag(picture, ShownKinds::Pictures); + QVERIFY(!picture->isVisible()); + } + + // Untagged items (symbols, wires) are never touched. + void untaggedUntouched() + { + auto symbol = add(); + auto hidden_symbol = add(); + hidden_symbol->setVisible(false); + for (int k = 0; k < ShownKinds::KindCount; ++k) { + ShownKinds::setShown(ShownKinds::Kind(k), false); + ShownKinds::apply(scene, ShownKinds::Kind(k)); + } + QVERIFY(symbol->isVisible()); + for (int k = 0; k < ShownKinds::KindCount; ++k) { + ShownKinds::setShown(ShownKinds::Kind(k), true); + ShownKinds::apply(scene, ShownKinds::Kind(k)); + } + QVERIFY(!hidden_symbol->isVisible()); + } + + // apply() touches only the kind it is given. + void applyOnlyThatKind() + { + auto shape = add(); + ShownKinds::tag(shape, ShownKinds::Shapes); + ShownKinds::setShown(ShownKinds::Shapes, false); + ShownKinds::apply(scene, ShownKinds::Tables); + QVERIFY(shape->isVisible()); + } + + // An item that hid itself (a wire number switched off) stays hidden + // when its kind is hidden and shown again. + void ownHiddenStaysHidden() + { + auto number = add(); + ShownKinds::tag(number, ShownKinds::WireNumbers); + ShownKinds::setVisible(number, false); + ShownKinds::setShown(ShownKinds::WireNumbers, false); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + ShownKinds::setShown(ShownKinds::WireNumbers, true); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + QVERIFY(!number->isVisible()); + } + + // An item asking to be shown while its kind is hidden stays hidden, + // and appears when the kind is shown (one text per potential moving + // to another wire, a cross-reference changing its snap). + void showVetoedThenRestored() + { + auto number = add(); + ShownKinds::tag(number, ShownKinds::WireNumbers); + ShownKinds::setShown(ShownKinds::WireNumbers, false); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + ShownKinds::setVisible(number, true); + QVERIFY(!number->isVisible()); + ShownKinds::setShown(ShownKinds::WireNumbers, true); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + QVERIFY(number->isVisible()); + } + + // Hidden while its kind is hidden: not shown again with the kind. + void hiddenWhileKindHidden() + { + auto number = add(); + ShownKinds::tag(number, ShownKinds::WireNumbers); + ShownKinds::setShown(ShownKinds::WireNumbers, false); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + ShownKinds::setVisible(number, false); + ShownKinds::setShown(ShownKinds::WireNumbers, true); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + QVERIFY(!number->isVisible()); + } + + // wantsVisible() tells the wire carrying a potential's text from the + // others while wire texts are hidden, so deleting it moves the text. + void wantsVisibleWhileKindHidden() + { + auto carrier = add(); + auto other = add(); + ShownKinds::tag(carrier, ShownKinds::WireNumbers); + ShownKinds::tag(other, ShownKinds::WireNumbers); + ShownKinds::setVisible(other, false); + ShownKinds::setShown(ShownKinds::WireNumbers, false); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + QVERIFY(!carrier->isVisible()); + QVERIFY(ShownKinds::wantsVisible(carrier)); + QVERIFY(!ShownKinds::wantsVisible(other)); + ShownKinds::setShown(ShownKinds::WireNumbers, true); + ShownKinds::apply(scene, ShownKinds::WireNumbers); + QVERIFY(ShownKinds::wantsVisible(carrier)); + QVERIFY(!ShownKinds::wantsVisible(other)); + } + + // A text under a hidden parent still counts as wanting to be visible. + void childOfHiddenParent() + { + auto group = add(); + auto text = add(group); + ShownKinds::tag(group, ShownKinds::SymbolTexts); + ShownKinds::tag(text, ShownKinds::CrossReferences); + ShownKinds::setShown(ShownKinds::SymbolTexts, false); + ShownKinds::apply(scene, ShownKinds::SymbolTexts); + ShownKinds::setShown(ShownKinds::CrossReferences, false); + ShownKinds::apply(scene, ShownKinds::CrossReferences); + ShownKinds::setShown(ShownKinds::SymbolTexts, true); + ShownKinds::apply(scene, ShownKinds::SymbolTexts); + QVERIFY(!text->isVisible()); + ShownKinds::setShown(ShownKinds::CrossReferences, true); + ShownKinds::apply(scene, ShownKinds::CrossReferences); + QVERIFY(text->isVisible()); + } + + // isHidden() follows the parents: a cross-reference under a symbol text. + void hiddenThroughParent() + { + auto symbol = add(); + auto label = add(symbol); + auto xref = add(label); + ShownKinds::tag(label, ShownKinds::SymbolTexts); + ShownKinds::tag(xref, ShownKinds::CrossReferences); + QVERIFY(!ShownKinds::isHidden(xref)); + + ShownKinds::setShown(ShownKinds::SymbolTexts, false); + QVERIFY(ShownKinds::isHidden(xref)); + QVERIFY(!ShownKinds::isHidden(symbol)); + } + + // Hidden means safe from Select All, copy and delete: Qt will not + // select it, and drops a selection it had. + void hiddenIsNotSelectable() + { + auto text = add(); + ShownKinds::tag(text, ShownKinds::FreeTexts); + text->setSelected(true); + ShownKinds::setShown(ShownKinds::FreeTexts, false); + ShownKinds::apply(scene, ShownKinds::FreeTexts); + QVERIFY(!text->isSelected()); + text->setSelected(true); + QVERIFY(scene->selectedItems().isEmpty()); + QVERIFY(scene->items(QRectF(-5, -5, 20, 20)).isEmpty()); + } +}; + +QTEST_MAIN(tst_shownkinds) +#include "tst_shownkinds.moc"