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 <kadet1090@gmail.com>
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
#include <Inventor/SbColor.h>
|
||||
|
||||
#include <App/Datums.h>
|
||||
#include <App/Document.h>
|
||||
#include <Gui/ViewParams.h>
|
||||
|
||||
#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()
|
||||
|
||||
@@ -29,10 +29,12 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QWidgetAction>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#include <App/Application.h>
|
||||
@@ -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<ConstraintItem*>(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<Sketcher::Constraint*>& 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<ConstraintItem*>(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<ConstraintItem*>(ui->listWidgetConstraints->item(i));
|
||||
auto* it = static_cast<ConstraintItem*>(ui->listWidgetConstraints->item(i));
|
||||
it->ConstraintNbr = i;
|
||||
it->value = QVariant();
|
||||
constraintMap[it->ConstraintNbr] = it;
|
||||
it->updateVirtualSpaceStatus();
|
||||
}
|
||||
ui->listWidgetConstraints->blockSignals(tmpBlock);
|
||||
|
||||
@@ -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<int, ConstraintItem*> constraintMap;
|
||||
|
||||
struct PendingSelectionUpdate
|
||||
{
|
||||
ConstraintItem* item;
|
||||
bool select;
|
||||
};
|
||||
std::vector<PendingSelectionUpdate> selectionBuffer;
|
||||
bool selectionUpdateTimerPending = false;
|
||||
|
||||
void processSelectionBuffer();
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
@@ -32,9 +32,11 @@
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QShortcut>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QWidgetAction>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -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<ElementItem*>(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<ElementItem*>(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<ElementItem*>(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<Part::Geometry*>& 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#ifndef GUI_TASKVIEW_TaskSketcherElements_H
|
||||
#define GUI_TASKVIEW_TaskSketcherElements_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <QListWidget>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
@@ -165,6 +166,19 @@ private:
|
||||
ElementFilterList* filterList;
|
||||
|
||||
bool isNamingBoxChecked;
|
||||
|
||||
// Buffering to speed up large selections
|
||||
std::unordered_map<int, ElementItem*> elementMap;
|
||||
|
||||
struct PendingUpdate
|
||||
{
|
||||
ElementItem* item;
|
||||
bool select;
|
||||
};
|
||||
std::vector<PendingUpdate> selectionBuffer;
|
||||
bool updateTimerPending = false;
|
||||
|
||||
void processSelectionBuffer();
|
||||
};
|
||||
|
||||
} // namespace SketcherGui
|
||||
|
||||
@@ -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<std::string> 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<Part::Geometry*>::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<std::string> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -451,6 +451,7 @@ private:
|
||||
std::set<int> SelPointSet; // Indices as PreselectPoint (and -1 for rootpoint)
|
||||
std::set<int> SelCurvSet; // also holds cross axes at -1 and -2
|
||||
std::set<int> SelConstraintSet; // ConstraintN, N = index + 1.
|
||||
bool selectionBuffering {false};
|
||||
};
|
||||
//@}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user