Fix the wire properties panel changing properties nobody edited

With "Show the properties of a selected conductor in the Selection
properties panel" switched on, editing a wire there could change things
the user never touched:

- Enter in a field (Function, Section...) is not used by the line edit,
  so it reaches the checkable "Multifilaire" group box around it, which
  takes it as a click. The wire was switched to single-line, gaining
  ground, neutral and phase symbols. The modal dialog never shows this
  because its OK button takes Enter first.
- Every edit wrote back the whole set of properties as the widget holds
  them, so a value the widget cannot show exactly was rewritten: a dash
  size of 1 became 2.

The panel now swallows Enter at its checkable group boxes, and writes
only the fields that differ from what it showed, through the new
ConductorProperties::applyChanges(). "Apply to all conductors of this
potential" uses the same rule, so the rest of the potential keeps its
own values too.

tst_conductorapplychanges checks each field on its own: change that one
field, and a wire whose every field differs takes it and keeps the rest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-01 23:10:15 +13:00
parent 7fdc314e41
commit a8699f6f62
6 changed files with 307 additions and 35 deletions
+67 -34
View File
@@ -34,6 +34,7 @@
#include <QCheckBox>
#include <QComboBox>
#include <QGroupBox>
#include <QKeyEvent>
#include <QLineEdit>
#include <QScrollArea>
#include <QSettings>
@@ -89,10 +90,35 @@ ConductorPropertiesEditorWidget::ConductorPropertiesEditorWidget(
// while keeping a minimum height so it stays usable when the dock is short.
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
setMinimumHeight(200);
//Enter in a field (Function, Section...) is not used by the field: it
//goes on to the checkable "Multifilaire" or "Unifilaire" group box
//around it, which takes it as a click and switches the wire between
//multi-line and single-line. The modal dialog never shows this, its
//OK button takes Enter; here nothing does, so stop it at the box.
for (auto *gb : m_cpw->findChildren<QGroupBox *>())
if (gb->isCheckable())
gb->installEventFilter(this);
setDisabled(true);
setConductor(conductor);
}
/**
@brief ConductorPropertiesEditorWidget::eventFilter
Swallow Enter on the checkable group boxes, see the constructor.
*/
bool ConductorPropertiesEditorWidget::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::KeyPress
|| event->type() == QEvent::KeyRelease)
{
const int key = static_cast<QKeyEvent *>(event)->key();
if (key == Qt::Key_Return || key == Qt::Key_Enter)
return true;
}
return PropertiesEditorWidget::eventFilter(watched, event);
}
ConductorPropertiesEditorWidget::~ConductorPropertiesEditorWidget()
{}
@@ -130,7 +156,7 @@ void ConductorPropertiesEditorWidget::apply()
if (!m_conductor || !m_conductor->diagram()) return;
if (QUndoCommand *undo = associatedUndo())
m_conductor->diagram()->undoStack().push(undo);
m_initial = m_conductor->properties();
m_shown = m_cpw->properties();
}
/**
@@ -212,13 +238,13 @@ void ConductorPropertiesEditorWidget::disconnectChangeSignals()
/**
@brief ConductorPropertiesEditorWidget::reset
Discard the in-progress edit, restoring the conductor's current properties.
Discard the in-progress edit, restoring what the widget showed.
*/
void ConductorPropertiesEditorWidget::reset()
{
if (!m_conductor) return;
m_updating = true;
m_cpw->setProperties(m_initial);
m_cpw->setProperties(m_shown);
m_updating = false;
}
@@ -230,54 +256,61 @@ void ConductorPropertiesEditorWidget::updateUi()
{
if (!m_conductor) return;
m_updating = true;
m_initial = m_conductor->properties();
m_cpw->setProperties(m_initial);
m_cpw->setProperties(m_conductor->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();
m_updating = false;
}
/**
@brief ConductorPropertiesEditorWidget::associatedUndo
@return the edit as a QPropertyUndoCommand, or nullptr if unchanged.
@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.
When "apply to all" is ticked, every conductor on the same potential is
updated in the same undo step (one undo reverts them all), exactly as the
modal dialog does (ConductorPropertiesDialog::PropertiesDialog). Otherwise
only the selected conductor is changed.
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_conductor->properties()) return nullptr;
if (new_properties == m_shown) return nullptr;
QVariant old_value, new_value;
old_value.setValue(m_conductor->properties());
new_value.setValue(new_properties);
auto *undo = new QPropertyUndoCommand(
m_conductor, "properties", old_value, new_value);
undo->setText(tr("Modifier les propriétés d'un conducteur", "undo caption"));
// Propagate to every conductor on the same potential, as the modal dialog
// does: each related conductor becomes a child command of the same undo
// step, set to the same target properties.
QList<Conductor *> 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;
auto *undo = new QUndoCommand();
int changed = 0;
for (Conductor *conductor : std::as_const(targets))
{
const auto potential = m_conductor->relatedPotentialConductors();
if (!potential.isEmpty())
{
undo->setText(tr("Modifier les propriétés de plusieurs conducteurs",
"undo caption"));
for (Conductor *potential_conductor : potential)
{
QVariant old_v;
old_v.setValue(potential_conductor->properties());
new QPropertyUndoCommand(
potential_conductor, "properties", old_v, new_value, undo);
}
}
const ConductorProperties old_properties = conductor->properties();
ConductorProperties properties = old_properties;
properties.applyChanges(m_shown, new_properties);
if (properties == old_properties) continue;
QVariant old_value, new_value;
old_value.setValue(old_properties);
new_value.setValue(properties);
new QPropertyUndoCommand(conductor, "properties", old_value, new_value, undo);
++changed;
}
if (!changed)
{
delete undo;
return nullptr;
}
undo->setText(changed == 1
? tr("Modifier les propriétés d'un conducteur", "undo caption")
: tr("Modifier les propriétés de plusieurs conducteurs", "undo caption"));
return undo;
}