From 6f90c5ea61d95fabb4437457feb07d3e6b3b9253 Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Tue, 18 Nov 2025 16:15:03 +0100 Subject: [PATCH] Sketcher: ConstraintList: prevent N transaction on box selection (#25254) --- .../Sketcher/Gui/TaskSketcherConstraints.cpp | 133 ++++++++++-------- .../Sketcher/Gui/TaskSketcherConstraints.h | 4 + 2 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index 060cbbf7d0..28f38ce0d8 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -822,6 +822,7 @@ TaskSketcherConstraints::TaskSketcherConstraints(ViewProviderSketch* sketchView) , specialFilterMode{SpecialFilterType::None} , sketchView(sketchView) , inEditMode(false) + , updateListPending(false) , ui(new Ui_TaskSketcherConstraints) { // we need a separate container widget to add all controls to @@ -1304,78 +1305,90 @@ void TaskSketcherConstraints::onSelectionChanged(const Gui::SelectionChanges& ms ui->listWidgetConstraints->clearSelection(); ui->listWidgetConstraints->blockSignals(tmpBlock); - if (specialFilterMode == SpecialFilterType::Selected) { - updateSelectionFilter(); + if (ui->filterBox->checkState() == Qt::Checked) { + if (specialFilterMode == SpecialFilterType::Selected) { + updateSelectionFilter(); - bool block = this->blockSelection(true);// avoid to be notified by itself - updateList(); - this->blockSelection(block); - } - else if (specialFilterMode == SpecialFilterType::Associated) { - associatedConstraintsFilter.clear(); - updateList(); + bool block = this->blockSelection(true);// avoid to be notified by itself + updateList(); + this->blockSelection(block); + } + else if (specialFilterMode == SpecialFilterType::Associated) { + associatedConstraintsFilter.clear(); + updateList(); + } } + return; } - else if (msg.Type == Gui::SelectionChanges::AddSelection - || msg.Type == Gui::SelectionChanges::RmvSelection) { - bool select = (msg.Type == Gui::SelectionChanges::AddSelection); - // is it this object?? - if (strcmp(msg.pDocName, sketchView->getSketchObject()->getDocument()->getName()) == 0 - && strcmp(msg.pObjectName, sketchView->getSketchObject()->getNameInDocument()) == 0) { - if (msg.pSubName) { - QRegularExpression rx(QStringLiteral("^Constraint(\\d+)$")); - QRegularExpressionMatch match; - QString expr = QString::fromLatin1(msg.pSubName); - boost::ignore_unused(expr.indexOf(rx, 0, &match)); - if (match.hasMatch()) {// is a constraint - bool ok; - int ConstrId = match.captured(1).toInt(&ok) - 1; - if (ok) { - int countItems = ui->listWidgetConstraints->count(); - for (int i = 0; i < countItems; i++) { - ConstraintItem* item = - static_cast(ui->listWidgetConstraints->item(i)); - if (item->ConstraintNbr == ConstrId) { - auto tmpBlock = ui->listWidgetConstraints->blockSignals(true); - item->setSelected(select); - ui->listWidgetConstraints->blockSignals(tmpBlock); - SketcherGui::scrollTo(ui->listWidgetConstraints, i, select); - break; - } - } - if (specialFilterMode == SpecialFilterType::Selected) { - updateSelectionFilter(); - bool block = - this->blockSelection(true);// avoid to be notified by itself - updateList(); - this->blockSelection(block); - } - } - } - else if (specialFilterMode == SpecialFilterType::Associated) {// is NOT a constraint - int geoid = Sketcher::GeoEnum::GeoUndef; - Sketcher::PointPos pointpos = Sketcher::PointPos::none; - getSelectionGeoId(expr, geoid, pointpos); + bool select = msg.Type == Gui::SelectionChanges::AddSelection; + if (!select && msg.Type != Gui::SelectionChanges::RmvSelection) { + return; + } - if (geoid != Sketcher::GeoEnum::GeoUndef - && pointpos == Sketcher::PointPos::none) { - // It is not possible to update on single addition/removal of a geometric - // element, as one removal may imply removing a constraint that should be - // added by a different element that is still selected. The necessary checks - // outweigh a full rebuild of the filter. - updateAssociatedConstraintsFilter(); - updateList(); - } + // is it this object?? + if (strcmp(msg.pDocName, sketchView->getSketchObject()->getDocument()->getName()) != 0 + || strcmp(msg.pObjectName, sketchView->getSketchObject()->getNameInDocument()) != 0 + || !msg.pSubName) { + return; + } + + QRegularExpression rx(QStringLiteral("^Constraint(\\d+)$")); + QRegularExpressionMatch match; + QString expr = QString::fromLatin1(msg.pSubName); + boost::ignore_unused(expr.indexOf(rx, 0, &match)); + if (match.hasMatch()) {// is a constraint + bool ok; + int ConstrId = match.captured(1).toInt(&ok) - 1; + if (ok) { + int countItems = ui->listWidgetConstraints->count(); + for (int i = 0; i < countItems; i++) { + ConstraintItem* item = + static_cast(ui->listWidgetConstraints->item(i)); + if (item->ConstraintNbr == ConstrId) { + auto tmpBlock = ui->listWidgetConstraints->blockSignals(true); + item->setSelected(select); + ui->listWidgetConstraints->blockSignals(tmpBlock); + SketcherGui::scrollTo(ui->listWidgetConstraints, i, select); + break; } } + + if (specialFilterMode == SpecialFilterType::Selected) { + updateSelectionFilter(); + bool block = + this->blockSelection(true);// avoid to be notified by itself + updateList(); + this->blockSelection(block); + } } } - else if (msg.Type == Gui::SelectionChanges::SetSelection) { - // do nothing here + else if (ui->filterBox->checkState() == Qt::Checked && specialFilterMode == SpecialFilterType::Associated) { + int geoid = Sketcher::GeoEnum::GeoUndef; + Sketcher::PointPos pointpos = Sketcher::PointPos::none; + getSelectionGeoId(expr, geoid, pointpos); + + if (geoid != Sketcher::GeoEnum::GeoUndef + && pointpos == Sketcher::PointPos::none) { + // It is not possible to update on single addition/removal of a geometric + // element, as one removal may imply removing a constraint that should be + // added by a different element that is still selected. The necessary checks + // outweigh a full rebuild of the filter. + if (!updateListPending) { + updateListPending = true; + QTimer::singleShot(0, this, &TaskSketcherConstraints::deferredUpdateList); + } + } } } +void TaskSketcherConstraints::deferredUpdateList() +{ + updateAssociatedConstraintsFilter(); + updateList(); + updateListPending = false; +} + void TaskSketcherConstraints::OnChange(Base::Subject& rCaller, const char* rcReason) { Q_UNUSED(rCaller); diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h index d793a576a9..f9682d24d4 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h @@ -203,9 +203,13 @@ protected: private: void onChangedSketchView(const Gui::ViewProvider&, const App::Property&); +private Q_SLOTS: + void deferredUpdateList(); + private: QWidget* proxy; bool inEditMode; + bool updateListPending; std::unique_ptr ui; ConstraintFilter::FilterValueBitset multiFilterStatus; // Stores the filters to be aggregated // to form the multifilter.