From 0fa707c5234ff7c2c65d1ce4587dcf48319aa40a Mon Sep 17 00:00:00 2001 From: PaddleStroke Date: Mon, 5 Jan 2026 18:41:45 +0100 Subject: [PATCH] Sketcher: Speed up large bulk Selection in edit (#26663) * Sketcher: Speed up large bulk Selection in edit * Update ViewProviderSketch.cpp * Update src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp --------- Co-authored-by: Kacper Donat --- src/Gui/ViewProviderPlane.cpp | 34 ++- .../Sketcher/Gui/TaskSketcherConstraints.cpp | 93 ++++--- .../Sketcher/Gui/TaskSketcherConstraints.h | 14 + src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 248 ++++++++---------- src/Mod/Sketcher/Gui/TaskSketcherElements.h | 14 + src/Mod/Sketcher/Gui/ViewProviderSketch.cpp | 87 ++++-- src/Mod/Sketcher/Gui/ViewProviderSketch.h | 1 + 7 files changed, 295 insertions(+), 196 deletions(-) diff --git a/src/Gui/ViewProviderPlane.cpp b/src/Gui/ViewProviderPlane.cpp index b14e2dd0e2..7c859f061d 100644 --- a/src/Gui/ViewProviderPlane.cpp +++ b/src/Gui/ViewProviderPlane.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include "ViewProviderPlane.h" @@ -158,13 +159,36 @@ void ViewProviderPlane::setLabelVisibility(bool val) labelSwitch->whichChild = val ? SO_SWITCH_ALL : SO_SWITCH_NONE; } -void ViewProviderPlane::onSelectionChanged(const SelectionChanges&) +void ViewProviderPlane::onSelectionChanged(const SelectionChanges& msg) { - isSelected = Gui::Selection().isSelected(getObject()); - isHovered = Gui::Selection().getPreselection().Object.getSubObject() == getObject() - || Gui::Selection().getPreselection().Object.getObject() == getObject(); + bool isSelectedOrHoveredBefore = isSelected || isHovered; - updatePlaneSize(); + if (msg.Type == Gui::SelectionChanges::ClrSelection) { + isSelected = false; + } + + auto obj = getObject(); + if (!obj) { + return; + } + auto doc = obj->getDocument(); + if (!doc) { + return; + } + const char* nameInDoc = getObject()->getNameInDocument(); + + if (nameInDoc && strcmp(msg.pDocName, doc->getName()) == 0 + && strcmp(msg.pObjectName, nameInDoc) == 0) { + isSelected = Gui::Selection().isSelected(getObject()); + isHovered = Gui::Selection().getPreselection().Object.getSubObject() == getObject() + || Gui::Selection().getPreselection().Object.getObject() == getObject(); + } + + bool isSelectedOrHovered = isSelected || isHovered; + + if (isSelectedOrHoveredBefore != isSelectedOrHovered) { + updatePlaneSize(); + } } void ViewProviderPlane::updatePlaneSize() diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index 11b5899fd7..e3da95d75a 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -29,10 +29,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -1335,39 +1337,23 @@ void TaskSketcherConstraints::onSelectionChanged(const Gui::SelectionChanges& ms 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 (std::strncmp(msg.pSubName, "Constraint", 10) == 0) { + int id = std::atoi(msg.pSubName + 10) - 1; - if (specialFilterMode == SpecialFilterType::Selected) { - updateSelectionFilter(); - bool block = - this->blockSelection(true);// avoid to be notified by itself - updateList(); - this->blockSelection(block); + auto it = constraintMap.find(id); + if (it != constraintMap.end()) { + selectionBuffer.push_back({it->second, select}); + + if (!selectionUpdateTimerPending) { + selectionUpdateTimerPending = true; + QTimer::singleShot(0, this, &TaskSketcherConstraints::processSelectionBuffer); } } } else if (ui->filterBox->checkState() == Qt::Checked && specialFilterMode == SpecialFilterType::Associated) { int geoid = Sketcher::GeoEnum::GeoUndef; Sketcher::PointPos pointpos = Sketcher::PointPos::none; + QString expr = QString::fromLatin1(msg.pSubName); getSelectionGeoId(expr, geoid, pointpos); if (geoid != Sketcher::GeoEnum::GeoUndef @@ -1384,6 +1370,43 @@ void TaskSketcherConstraints::onSelectionChanged(const Gui::SelectionChanges& ms } } +void TaskSketcherConstraints::processSelectionBuffer() +{ + selectionUpdateTimerPending = false; + if (selectionBuffer.empty()) { + return; + } + + QSignalBlocker block(ui->listWidgetConstraints); + + QItemSelection selectionObj; + for (const auto& update : selectionBuffer) { + // NOTE: We trust the buffer has valid pointers (lifetime matches widget) + if (update.select) { + QModelIndex idx = ui->listWidgetConstraints->model()->index(ui->listWidgetConstraints->row(update.item), 0); + selectionObj.select(idx, idx); + } else { + update.item->setSelected(false); + } + } + ui->listWidgetConstraints->selectionModel()->select(selectionObj, QItemSelectionModel::Select); + + // Scroll only if single item selected + if (selectionBuffer.size() == 1 && selectionBuffer[0].select) { + SketcherGui::scrollTo(ui->listWidgetConstraints, ui->listWidgetConstraints->row(selectionBuffer[0].item), true); + } + + if (specialFilterMode == SpecialFilterType::Selected) { + updateSelectionFilter(); + // avoid to be notified by itself + bool block = this->blockSelection(true); + updateList(); + this->blockSelection(block); + } + + selectionBuffer.clear(); +} + void TaskSketcherConstraints::deferredUpdateList() { updateAssociatedConstraintsFilter(); @@ -1683,20 +1706,13 @@ bool TaskSketcherConstraints::isConstraintFiltered(QListWidgetItem* item) void TaskSketcherConstraints::slotConstraintsChanged() { assert(sketchView); + + constraintMap.clear(); + // Build up ListView with the constraints const Sketcher::SketchObject* sketch = sketchView->getSketchObject(); const std::vector& vals = sketch->Constraints.getValues(); - /* Update constraint number and virtual space check status */ - for (int i = 0; i < ui->listWidgetConstraints->count(); ++i) { - ConstraintItem* it = dynamic_cast(ui->listWidgetConstraints->item(i)); - - assert(it); - - it->ConstraintNbr = i; - it->value = QVariant(); - } - /* Remove entries, if any */ for (std::size_t i = ui->listWidgetConstraints->count(); i > vals.size(); --i) delete ui->listWidgetConstraints->takeItem(i - 1); @@ -1708,7 +1724,10 @@ void TaskSketcherConstraints::slotConstraintsChanged() /* Update the states */ auto tmpBlock = ui->listWidgetConstraints->blockSignals(true); for (int i = 0; i < ui->listWidgetConstraints->count(); ++i) { - ConstraintItem* it = static_cast(ui->listWidgetConstraints->item(i)); + auto* it = static_cast(ui->listWidgetConstraints->item(i)); + it->ConstraintNbr = i; + it->value = QVariant(); + constraintMap[it->ConstraintNbr] = it; it->updateVirtualSpaceStatus(); } ui->listWidgetConstraints->blockSignals(tmpBlock); diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h index aa940c888b..5670d50d42 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.h @@ -34,6 +34,7 @@ #include "ConstraintFilters.h" +class ConstraintItem; namespace App { @@ -221,6 +222,19 @@ private: // selected geometry ConstraintFilterList* filterList; boost::signals2::scoped_connection changedSketchView; + + // Buffering structures + std::unordered_map constraintMap; + + struct PendingSelectionUpdate + { + ConstraintItem* item; + bool select; + }; + std::vector selectionBuffer; + bool selectionUpdateTimerPending = false; + + void processSelectionBuffer(); }; } // namespace SketcherGui diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index 548d2863c8..98d5cefe6d 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -32,9 +32,11 @@ #include #include #include +#include #include #include #include +#include #include @@ -1480,150 +1482,121 @@ void TaskSketcherElements::updateVisibility() } } -/*------------------*/ -// clang-format on void TaskSketcherElements::onSelectionChanged(const Gui::SelectionChanges& msg) { - // update the listwidget - auto updateListWidget = [this](auto& modified_item) { - QSignalBlocker sigblk(this->ui->listWidgetElements); - if (modified_item == nullptr) { - return; - } - bool is_selected = modified_item->isSelected(); - const bool should_be_selected = modified_item->isLineSelected - || modified_item->isStartingPointSelected || modified_item->isEndPointSelected - || modified_item->isMidPointSelected; + if (msg.Type == Gui::SelectionChanges::ClrSelection) { + clearWidget(); + return; + } - // If an element is already selected and a new subelement gets selected - // (eg., if you select the arc of a circle then select the center as - // well), the new subelement won't get highlighted in the list until you - // mouseover the list. To avoid this, we deselect first to trigger a - // redraw. - if (should_be_selected && is_selected) { - modified_item->setSelected(false); - is_selected = false; - } + if (msg.Type != Gui::SelectionChanges::AddSelection + && msg.Type != Gui::SelectionChanges::RmvSelection) { + return; + } - if (should_be_selected != is_selected) { - modified_item->setSelected(should_be_selected); - } - }; + // is it this object?? + if (strcmp(msg.pDocName, sketchView->getSketchObject()->getDocument()->getName()) != 0 + || strcmp(msg.pObjectName, sketchView->getSketchObject()->getNameInDocument()) != 0 + || !msg.pSubName) { + return; + } - switch (msg.Type) { - case Gui::SelectionChanges::ClrSelection: { - clearWidget(); - return; - } - case Gui::SelectionChanges::AddSelection: - case 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) { - return; - } - if (!msg.pSubName) { - return; - } - ElementItem* modified_item = nullptr; - QString expr = QString::fromLatin1(msg.pSubName); - std::string shapetype(msg.pSubName); - // if-else edge vertex - if (shapetype.starts_with("Edge")) { - QRegularExpression rx(QStringLiteral("^Edge(\\d+)$")); - QRegularExpressionMatch match; - boost::ignore_unused(expr.indexOf(rx, 0, &match)); - if (!match.hasMatch()) { - return; - } - bool ok; - int ElementId = match.captured(1).toInt(&ok) - 1; - if (!ok) { - return; - } - int countItems = ui->listWidgetElements->count(); - // TODO: This and the loop below get slow when we have a lot of items. - // Perhaps we should also maintain a map so that we can look up items - // by element number. - for (int i = 0; i < countItems; i++) { - auto* item = static_cast(ui->listWidgetElements->item(i)); - if (item->ElementNbr == ElementId) { - item->isLineSelected = select; - modified_item = item; - SketcherGui::scrollTo(ui->listWidgetElements, i, select); - break; - } - } - } - else if (shapetype.starts_with("ExternalEdge")) { - QRegularExpression rx(QStringLiteral("^ExternalEdge(\\d+)$")); - QRegularExpressionMatch match; - boost::ignore_unused(expr.indexOf(rx, 0, &match)); - if (!match.hasMatch()) { - return; - } - bool ok; - int ElementId = -match.captured(1).toInt(&ok) - 2; - if (!ok) { - return; - } - int countItems = ui->listWidgetElements->count(); - for (int i = 0; i < countItems; i++) { - auto* item = static_cast(ui->listWidgetElements->item(i)); - if (item->ElementNbr == ElementId) { - item->isLineSelected = select; - modified_item = item; - break; - } - } - } - else if (shapetype.starts_with("Vertex")) { - QRegularExpression rx(QStringLiteral("^Vertex(\\d+)$")); - QRegularExpressionMatch match; - boost::ignore_unused(expr.indexOf(rx, 0, &match)); - if (!match.hasMatch()) { - return; - } - bool ok; - int ElementId = match.captured(1).toInt(&ok) - 1; - if (!ok) { - return; - } - // Get the GeoID&Pos - int GeoId; - Sketcher::PointPos PosId; - sketchView->getSketchObject()->getGeoVertexIndex(ElementId, GeoId, PosId); - int countItems = ui->listWidgetElements->count(); - for (int i = 0; i < countItems; i++) { - auto* item = static_cast(ui->listWidgetElements->item(i)); - if (item->ElementNbr == GeoId) { - modified_item = item; - switch (PosId) { - case Sketcher::PointPos::start: - item->isStartingPointSelected = select; - break; - case Sketcher::PointPos::end: - item->isEndPointSelected = select; - break; - case Sketcher::PointPos::mid: - item->isMidPointSelected = select; - break; - default: - break; - } - break; - } - } - } - updateListWidget(modified_item); + bool select = (msg.Type == Gui::SelectionChanges::AddSelection); + const char* subName = msg.pSubName; + ElementItem* modified_item = nullptr; + + // Format: "Edge123" -> offset 4 + if (std::strncmp(subName, "Edge", 4) == 0) { + int id = std::atoi(subName + 4) - 1; + auto it = elementMap.find(id); + if (it != elementMap.end()) { + modified_item = it->second; + modified_item->isLineSelected = select; + } + } + // Format: "ExternalEdge123" -> offset 12 + else if (std::strncmp(subName, "ExternalEdge", 12) == 0) { + int id = -std::atoi(subName + 12) - 2; + auto it = elementMap.find(id); + if (it != elementMap.end()) { + modified_item = it->second; + modified_item->isLineSelected = select; + } + } + // Format: "Vertex123" -> offset 6 + else if (std::strncmp(subName, "Vertex", 6) == 0) { + int vtId = std::atoi(subName + 6) - 1; + int GeoId; + Sketcher::PointPos PosId; + sketchView->getSketchObject()->getGeoVertexIndex(vtId, GeoId, PosId); + + auto it = elementMap.find(GeoId); + if (it != elementMap.end()) { + modified_item = it->second; + switch (PosId) { + case Sketcher::PointPos::start: + modified_item->isStartingPointSelected = select; + break; + case Sketcher::PointPos::end: + modified_item->isEndPointSelected = select; + break; + case Sketcher::PointPos::mid: + modified_item->isMidPointSelected = select; + break; + default: + break; + } + } + } + + // 3. Queue the UI update instead of applying immediately + if (modified_item) { + selectionBuffer.push_back({modified_item, select}); + + if (!updateTimerPending) { + updateTimerPending = true; + // Schedule processing for the next event loop cycle (0ms) + QTimer::singleShot(0, this, &TaskSketcherElements::processSelectionBuffer); } - default: - return; } } -// clang-format off + +void TaskSketcherElements::processSelectionBuffer() +{ + updateTimerPending = false; + if (selectionBuffer.empty()) { + return; + } + + QSignalBlocker sigblk(ui->listWidgetElements); + + QItemSelection selectionObj; + for (const auto& update : selectionBuffer) { + bool should_be = update.item->isLineSelected || + update.item->isStartingPointSelected || + update.item->isEndPointSelected || + update.item->isMidPointSelected; + + if (should_be) { + QModelIndex idx = ui->listWidgetElements->model()->index(ui->listWidgetElements->row(update.item), 0); + selectionObj.select(idx, idx); + } + else { + update.item->setSelected(false); + } + } + + // Apply all selections in ONE go + ui->listWidgetElements->selectionModel()->select(selectionObj, QItemSelectionModel::Select); + + // Only scroll if a single item was clicked/selected + if (selectionBuffer.size() == 1 && selectionBuffer[0].select) { + SketcherGui::scrollTo(ui->listWidgetElements, ui->listWidgetElements->row(selectionBuffer[0].item), true); + } + + selectionBuffer.clear(); +} void TaskSketcherElements::onListWidgetElementsItemPressed(QListWidgetItem* it) { @@ -1901,6 +1874,7 @@ void TaskSketcherElements::slotElementsChanged() const std::vector& vals = sketch->Geometry.getValues(); ui->listWidgetElements->clear(); + elementMap.clear(); using GeometryState = ElementItem::GeometryState; @@ -2008,6 +1982,8 @@ void TaskSketcherElements::slotElementsChanged() ui->listWidgetElements->addItem(itemN); + elementMap[itemN->ElementNbr] = itemN; + setItemVisibility(itemN); } @@ -2112,6 +2088,8 @@ void TaskSketcherElements::slotElementsChanged() ui->listWidgetElements->addItem(itemN); + elementMap[itemN->ElementNbr] = itemN; + setItemVisibility(itemN); } } diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.h b/src/Mod/Sketcher/Gui/TaskSketcherElements.h index 7fb9683a60..131be1b48c 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.h @@ -25,6 +25,7 @@ #ifndef GUI_TASKVIEW_TaskSketcherElements_H #define GUI_TASKVIEW_TaskSketcherElements_H +#include #include #include @@ -165,6 +166,19 @@ private: ElementFilterList* filterList; bool isNamingBoxChecked; + + // Buffering to speed up large selections + std::unordered_map elementMap; + + struct PendingUpdate + { + ElementItem* item; + bool select; + }; + std::vector selectionBuffer; + bool updateTimerPending = false; + + void processSelectionBuffer(); }; } // namespace SketcherGui diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index 5f93496c8a..b4353f2aec 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -2296,7 +2296,9 @@ void ViewProviderSketch::onSelectionChanged(const Gui::SelectionChanges& msg) int ConstrId = Sketcher::PropertyConstraintList::getIndexFromConstraintName(shapetype); selection.SelConstraintSet.insert(ConstrId); - editCoinManager->drawConstraintIcons(); + if (!selection.selectionBuffering) { + editCoinManager->drawConstraintIcons(); + } } updateColor(); } @@ -2594,13 +2596,21 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s& startPos, const SbVec2s& if (corners[0].getValue()[0] > corners[1].getValue()[0]) touchMode = true; - auto selectVertex = [this](int vertexid) { - std::stringstream ss; - ss << "Vertex" << vertexid; - addSelection2(ss.str()); + std::vector batchSelection; + batchSelection.reserve(geomlist.size()); + + auto addConvertedName = [this, sketchObject, &batchSelection](const std::string& suffix) { + std::string finalName = editSubName + sketchObject->convertSubName(suffix); + batchSelection.push_back(finalName); }; - auto selectEdge = [this](int edgeid) { + auto selectVertex = [&addConvertedName](int vertexid) { + std::stringstream ss; + ss << "Vertex" << vertexid; + addConvertedName(ss.str()); + }; + + auto selectEdge = [&addConvertedName](int edgeid) { std::stringstream ss; if (edgeid >= 0) { ss << "Edge" << edgeid; @@ -2608,7 +2618,7 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s& startPos, const SbVec2s& else { ss << "ExternalEdge" << -edgeid - 1; } - addSelection2(ss.str()); + addConvertedName(ss.str()); }; auto selectVertexIfInsideBox = [&polygon, &VertexId, &selectVertex](const Base::Vector3d & point) { @@ -2651,6 +2661,8 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s& startPos, const SbVec2s& } }; + selection.selectionBuffering = true; + for (std::vector::const_iterator it = geomlist.begin(); it != geomlist.end() - 2; ++it, ++GeoId) { @@ -2753,17 +2765,29 @@ void ViewProviderSketch::doBoxSelection(const SbVec2s& startPos, const SbVec2s& Base::Vector3d pnt0 = proj(Plm.getPosition()); if (polygon.Contains(Base::Vector2d(pnt0.x, pnt0.y))) { - std::stringstream ss; - ss << "RootPoint"; - addSelection2(ss.str()); + addConvertedName("RootPoint"); } + + if (!batchSelection.empty()) { + Gui::Selection().addSelections( + getSketchObject()->getDocument()->getName(), + getSketchObject()->getNameInDocument(), + batchSelection + ); + } + + selection.selectionBuffering = false; + editCoinManager->drawConstraintIcons(); + updateColor(); } void ViewProviderSketch::updateColor() { assert(isInEditMode()); - editCoinManager->updateColor(); + if (!selection.selectionBuffering) { + editCoinManager->updateColor(); + } } bool ViewProviderSketch::selectAll() @@ -2809,6 +2833,18 @@ bool ViewProviderSketch::selectAll() Gui::Selection().clearSelection(); + std::vector batchSelection; + // Heuristic reservation: Geometry count * 3 (start/end/edge) + Constraint count + batchSelection.reserve( + sketchObject->Geometry.getValues().size() * 3 + sketchObject->Constraints.getSize() + ); + + auto addConvertedName = [this, sketchObject, &batchSelection](const std::string& suffix) { + std::string finalName = editSubName + sketchObject->convertSubName(suffix); + batchSelection.push_back(finalName); + }; + + selection.selectionBuffering = true; if (focusOnElementWidget || noWidgetSelected) { int intGeoCount = sketchObject->getHighestCurveIndex() + 1; int extGeoCount = sketchObject->getExternalGeometryCount(); @@ -2817,16 +2853,17 @@ bool ViewProviderSketch::selectAll() int GeoId = 0; - auto selectVertex = [this](int geoId, Sketcher::PointPos pos) { + auto selectVertex = [this, &addConvertedName](int geoId, Sketcher::PointPos pos) { int vertexId = this->getSketchObject()->getVertexIndexGeoPos(geoId, pos); - addSelection2(fmt::format("Vertex{}", vertexId + 1)); + addConvertedName(fmt::format("Vertex{}", vertexId + 1)); }; - auto selectEdge = [this](int GeoId) { + auto selectEdge = [&addConvertedName](int GeoId) { if (GeoId >= 0) { - addSelection2(fmt::format("Edge{}", GeoId + 1)); - } else { - addSelection2(fmt::format("ExternalEdge{}", GeoEnum::RefExt - GeoId + 1)); + addConvertedName(fmt::format("Edge{}", GeoId + 1)); + } + else { + addConvertedName(fmt::format("ExternalEdge{}", GeoEnum::RefExt - GeoId + 1)); } }; @@ -2868,7 +2905,7 @@ bool ViewProviderSketch::selectAll() } if (!focusOnElementWidget) { - addSelection2("RootPoint"); + addConvertedName("RootPoint"); } if (hasUnselectedGeometry) { @@ -2882,10 +2919,22 @@ bool ViewProviderSketch::selectAll() if (focusedList && std::ranges::find(ids, i) == ids.end()) { continue; } - addSelection2(fmt::format("Constraint{}", i + 1)); + addConvertedName(fmt::format("Constraint{}", i + 1)); } } + if (!batchSelection.empty()) { + Gui::Selection().addSelections( + getSketchObject()->getDocument()->getName(), + getSketchObject()->getNameInDocument(), + batchSelection + ); + } + + selection.selectionBuffering = false; + editCoinManager->drawConstraintIcons(); + updateColor(); + return true; } diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.h b/src/Mod/Sketcher/Gui/ViewProviderSketch.h index 7d4574f55d..df8f93d033 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.h +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.h @@ -451,6 +451,7 @@ private: std::set SelPointSet; // Indices as PreselectPoint (and -1 for rootpoint) std::set SelCurvSet; // also holds cross axes at -1 and -2 std::set SelConstraintSet; // ConstraintN, N = index + 1. + bool selectionBuffering {false}; }; //@}