diff --git a/sources/factory/propertieseditorfactory.cpp b/sources/factory/propertieseditorfactory.cpp index 48a39738b..83b554f79 100644 --- a/sources/factory/propertieseditorfactory.cpp +++ b/sources/factory/propertieseditorfactory.cpp @@ -82,22 +82,28 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor( PropertiesEditorWidget *editor, QWidget *parent) { + //Selecting a conductor's text label edits its parent conductor (#500), + //mirroring how double-clicking the label opens the conductor dialog. + //A rubber band over wires selects their labels too: each label + //counts as its conductor, once. + QList mapped; + for (QGraphicsItem *qgi : std::as_const(items)) { + if (auto *cti = qgraphicsitem_cast(qgi)) { + if (Conductor *parent_cond = cti->parentConductor()) { + qgi = parent_cond; + } + } + if (!mapped.contains(qgi)) { + mapped << qgi; + } + } + items = mapped; + const int count_ = items.size(); if (count_ == 0) { return nullptr; } QGraphicsItem *item = items.first(); - - //Selecting a conductor's text label edits its parent conductor (#500), - //mirroring how double-clicking the label opens the conductor dialog. - if (count_ == 1) { - if (auto *cti = qgraphicsitem_cast(item)) { - if (Conductor *parent_cond = cti->parentConductor()) { - items = {parent_cond}; - item = parent_cond; - } - } - } const int type_ = item->type(); //The editor widget can only edit one item @@ -124,18 +130,17 @@ PropertiesEditorWidget *PropertiesEditorFactory::propertiesEditor( false).toBool()) { return nullptr; } - //Prototype (#500): single-conductor editing in the dock. - if (count_ > 1) { - return nullptr; + QList conductors; + for (QGraphicsItem *qgi : std::as_const(items)) { + conductors << static_cast(qgi); } - auto conductor = static_cast(item); if (class_name == ConductorPropertiesEditorWidget::staticMetaObject.className()) { - static_cast(editor)->setConductor(conductor); + static_cast(editor)->setConductors(conductors); return editor; } - return new ConductorPropertiesEditorWidget(conductor, parent); + return new ConductorPropertiesEditorWidget(conductors, parent); } case Element::Type: //1000 { diff --git a/sources/ui/conductorpropertieseditorwidget.cpp b/sources/ui/conductorpropertieseditorwidget.cpp index 8b50fa02d..af3e0be33 100644 --- a/sources/ui/conductorpropertieseditorwidget.cpp +++ b/sources/ui/conductorpropertieseditorwidget.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -44,11 +45,11 @@ /** @brief ConductorPropertiesEditorWidget::ConductorPropertiesEditorWidget - @param conductor : conductor to edit + @param conductors : conductors to edit @param parent : parent widget */ ConductorPropertiesEditorWidget::ConductorPropertiesEditorWidget( - Conductor *conductor, QWidget *parent) : + const QList &conductors, QWidget *parent) : PropertiesEditorWidget(parent), m_cpw(new ConductorPropertiesWidget(this)) { @@ -76,12 +77,19 @@ ConductorPropertiesEditorWidget::ConductorPropertiesEditorWidget( QStringLiteral("diagrameditor/conductor_apply_all"), on); }); + //Shown only when several conductors are selected: the fields hold + //the first one's values, and only the edited ones reach the others. + m_count_label = new QLabel(this); + m_count_label->setWordWrap(true); + m_count_label->hide(); + auto *scroll = new QScrollArea(this); scroll->setWidgetResizable(true); scroll->setFrameShape(QFrame::NoFrame); scroll->setWidget(m_cpw); auto *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(m_count_label); layout->addWidget(m_apply_all_cb); layout->addWidget(scroll); setMinimumWidth(120); @@ -100,7 +108,7 @@ ConductorPropertiesEditorWidget::ConductorPropertiesEditorWidget( gb->installEventFilter(this); setDisabled(true); - setConductor(conductor); + setConductors(conductors); } /** @@ -123,26 +131,56 @@ ConductorPropertiesEditorWidget::~ConductorPropertiesEditorWidget() {} /** - @brief ConductorPropertiesEditorWidget::setConductor - Set (or change) the conductor whose properties are edited. - @param conductor + @brief ConductorPropertiesEditorWidget::setConductors + Set (or change) the conductors whose properties are edited. + @param conductors */ -void ConductorPropertiesEditorWidget::setConductor(Conductor *conductor) +void ConductorPropertiesEditorWidget::setConductors( + const QList &conductors) { - if (!conductor) return; - if (m_conductor && m_conductor != conductor) - disconnect(m_conductor, &Conductor::propertiesChange, - this, &ConductorPropertiesEditorWidget::updateUi); - m_conductor = conductor; - //Keep the dock in sync when the conductor is edited elsewhere (e.g. the - //modal "Edit conductor" dialog); otherwise a stale snapshot would be - //written back on the next apply() and overwrite that change (issue #500). - connect(m_conductor, &Conductor::propertiesChange, - this, &ConductorPropertiesEditorWidget::updateUi, Qt::UniqueConnection); + if (conductors.isEmpty()) return; + + for (const QPointer &c : std::as_const(m_conductors)) + if (c) + disconnect(c, &Conductor::propertiesChange, + this, &ConductorPropertiesEditorWidget::updateUi); + m_conductors.clear(); + + for (Conductor *c : conductors) + { + m_conductors << c; + //Keep the dock in sync when a conductor is edited elsewhere (e.g. + //the modal "Edit conductor" dialog); otherwise a stale snapshot + //would be written back on the next apply() and overwrite that + //change (issue #500). + connect(c, &Conductor::propertiesChange, + this, &ConductorPropertiesEditorWidget::updateUi, + Qt::UniqueConnection); + } + + const int count = conductors.size(); + m_count_label->setText( + tr("%n conducteurs sélectionnés : seuls les champs modifiés leur sont appliqués.", + "selection properties panel", count)); + m_count_label->setVisible(count > 1); + setEnabled(true); updateUi(); } +/** + @brief ConductorPropertiesEditorWidget::firstConductor + @return the first edited conductor still alive, its values are the ones + shown, or nullptr +*/ +Conductor *ConductorPropertiesEditorWidget::firstConductor() const +{ + for (const QPointer &c : m_conductors) + if (c) + return c; + return nullptr; +} + /** @brief ConductorPropertiesEditorWidget::apply Push the edit onto the diagram's undo stack. @@ -153,9 +191,10 @@ void ConductorPropertiesEditorWidget::apply() // programmatically (updateUi/reset): mid-load the widget holds a partial // state that must not be committed onto the conductor. if (m_updating) return; - if (!m_conductor || !m_conductor->diagram()) return; + Conductor *first = firstConductor(); + if (!first || !first->diagram()) return; if (QUndoCommand *undo = associatedUndo()) - m_conductor->diagram()->undoStack().push(undo); + first->diagram()->undoStack().push(undo); m_shown = m_cpw->properties(); } @@ -242,7 +281,7 @@ void ConductorPropertiesEditorWidget::disconnectChangeSignals() */ void ConductorPropertiesEditorWidget::reset() { - if (!m_conductor) return; + if (!firstConductor()) return; m_updating = true; m_cpw->setProperties(m_shown); m_updating = false; @@ -250,13 +289,15 @@ void ConductorPropertiesEditorWidget::reset() /** @brief ConductorPropertiesEditorWidget::updateUi - Reload the widget from the conductor (e.g. when the selection changes). + Reload the widget from the first conductor (e.g. when the selection + changes). */ void ConductorPropertiesEditorWidget::updateUi() { - if (!m_conductor) return; + Conductor *first = firstConductor(); + if (!first) return; m_updating = true; - m_cpw->setProperties(m_conductor->properties()); + m_cpw->setProperties(first->properties()); //Read back rather than keep the conductor's own values: a value the //widget cannot show exactly must not count as an edit. m_shown = m_cpw->properties(); @@ -267,25 +308,32 @@ void ConductorPropertiesEditorWidget::updateUi() @brief ConductorPropertiesEditorWidget::associatedUndo @return the edit as one undo step, or nullptr if nothing changes. - Only the fields the user changed are written: the conductor keeps every - other value, including one the widget cannot show exactly. + Only the fields the user changed are applied, to each edited conductor: + with several conductors selected, each keeps its own text, function, + cable... unless that is the field being edited. - When "apply to all" is ticked, every conductor on the same potential is - updated too, in the same undo step (one undo reverts them all), as the - modal dialog does (ConductorPropertiesDialog::PropertiesDialog). + When "apply to all" is ticked, every conductor on the same potential as + an edited one is updated too, in the same undo step (one undo reverts + them all), as the modal dialog does + (ConductorPropertiesDialog::PropertiesDialog). */ QUndoCommand *ConductorPropertiesEditorWidget::associatedUndo() const { - if (!m_conductor) return nullptr; - const ConductorProperties new_properties = m_cpw->properties(); if (new_properties == m_shown) return nullptr; - QList targets {m_conductor}; - if (m_apply_all_cb && m_apply_all_cb->isChecked()) - for (Conductor *potential_conductor : m_conductor->relatedPotentialConductors()) - if (!targets.contains(potential_conductor)) - targets << potential_conductor; + const bool apply_all = m_apply_all_cb && m_apply_all_cb->isChecked(); + QList targets; + for (const QPointer &c : m_conductors) + { + if (!c) continue; + if (!targets.contains(c.data())) + targets << c.data(); + if (apply_all) + for (Conductor *potential_conductor : c->relatedPotentialConductors()) + if (!targets.contains(potential_conductor)) + targets << potential_conductor; + } auto *undo = new QUndoCommand(); int changed = 0; diff --git a/sources/ui/conductorpropertieseditorwidget.h b/sources/ui/conductorpropertieseditorwidget.h index a5f517738..f0b0d0d0c 100644 --- a/sources/ui/conductorpropertieseditorwidget.h +++ b/sources/ui/conductorpropertieseditorwidget.h @@ -23,18 +23,22 @@ #include #include +#include class Conductor; class ConductorPropertiesWidget; class QCheckBox; +class QLabel; /** @brief The ConductorPropertiesEditorWidget class Hosts the existing ConductorPropertiesWidget in the dockable selection- - properties panel, so a selected conductor can be edited in place like the + properties panel, so selected conductors can be edited in place like the other item types, instead of only through the modal dialog (issue #500). A pinned "apply to all conductors of the potential" checkbox (persisted) mirrors the modal dialog's option to propagate edits to the whole potential. + When several conductors are selected, the panel shows the first one and + applies to each only the fields the user changed. */ class ConductorPropertiesEditorWidget : public PropertiesEditorWidget { @@ -42,10 +46,11 @@ class ConductorPropertiesEditorWidget : public PropertiesEditorWidget public: explicit ConductorPropertiesEditorWidget( - Conductor *conductor = nullptr, QWidget *parent = nullptr); + const QList &conductors = {}, + QWidget *parent = nullptr); ~ConductorPropertiesEditorWidget() override; - void setConductor(Conductor *conductor); + void setConductors(const QList &conductors); void apply() override; void reset() override; @@ -60,11 +65,13 @@ class ConductorPropertiesEditorWidget : public PropertiesEditorWidget private: void connectChangeSignals(); void disconnectChangeSignals(); + Conductor *firstConductor() const; private: ConductorPropertiesWidget *m_cpw = nullptr; QCheckBox *m_apply_all_cb = nullptr; - Conductor *m_conductor = nullptr; + QLabel *m_count_label = nullptr; + QList> m_conductors; //What the widget showed before the edit: the fields that //differ from it are the ones the user changed. ConductorProperties m_shown;