diff --git a/src/Gui/Selection.cpp b/src/Gui/Selection.cpp index 2c6e81c08269..961913f651cd 100644 --- a/src/Gui/Selection.cpp +++ b/src/Gui/Selection.cpp @@ -33,6 +33,8 @@ # include #endif +#include + /// Here the FreeCAD includes sorted by Base,App,Gui...... #include "Application.h" #include "Document.h" @@ -630,8 +632,16 @@ void SelectionSingleton::slotSelectionChanged(const SelectionChanges& msg) { bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z, bool signal) { - if (DocName != "") + if(!pDocName || !pObjectName) { rmvPreselect(); + return false; + } + if(!pSubName) pSubName = ""; + + if(DocName==pDocName && FeatName==pObjectName && SubName==pSubName) + return true; + + rmvPreselect(); if (ActiveGate && !signal) { App::Document* pDoc = getDocument(pDocName); @@ -688,6 +698,12 @@ bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectN Notify(Chng); signalSelectionChanged(Chng); + if(signal) { + Chng.Type = SelectionChanges::SetPreselect; + Notify(Chng); + signalSelectionChanged(Chng); + } + // allows the preselection return true; } @@ -717,8 +733,7 @@ void SelectionSingleton::rmvPreselect() if (DocName == "") return; - SelectionChanges Chng(SelectionChanges::RmvPreselect, - DocName,FeatName,SubName); + SelectionChanges Chng(SelectionChanges::RmvPreselect,DocName,FeatName,SubName); // reset the current preselection CurrentPreselection = SelectionChanges(); @@ -943,6 +958,13 @@ bool SelectionSingleton::updateSelection(bool show, const char* pDocName, { if(!pDocName || !pObjectName) return false; + if(!pSubName) + pSubName = ""; + if(show && DocName==pDocName && FeatName==pObjectName && SubName==pSubName) { + SelectionChanges Chng(SelectionChanges::SetPreselectSignal,DocName,FeatName,SubName); + Notify(Chng); + signalSelectionChanged(Chng); + } if (!isSelected(pDocName, pObjectName, pSubName)) return false; auto pDoc = getDocument(pDocName); @@ -1216,7 +1238,7 @@ void SelectionSingleton::clearCompleteSelection() } bool SelectionSingleton::isSelected(const char* pDocName, - const char* pObjectName, const char* pSubName, bool resolve) const + const char* pObjectName, const char* pSubName, int resolve) const { if(!pObjectName) return false; auto pDoc = getDocument(pDocName); @@ -1226,7 +1248,7 @@ bool SelectionSingleton::isSelected(const char* pDocName, return isSelected(pDoc->getObject(pObjectName),pSubName,resolve); } -bool SelectionSingleton::isSelected(App::DocumentObject* pObject, const char* pSubName, bool resolve) const +bool SelectionSingleton::isSelected(App::DocumentObject* pObject, const char* pSubName, int resolve) const { if(!pObject || !pObject->getNameInDocument() || !pObject->getDocument()) return false; @@ -1239,10 +1261,12 @@ bool SelectionSingleton::isSelected(App::DocumentObject* pObject, const char* pS if(!pResolvedObject) return false; std::string subname; - if(pSubName) { - if(element && elementName.first.size()) { + std::string prefix; + if(pSubName && element) { + prefix = std::string(pSubName, element-pSubName); + if(elementName.first.size()) { // make sure the selected sub name is a new style if available - subname = std::string(pSubName,element-pSubName) + elementName.first; + subname = prefix + elementName.first; pSubName = subname.c_str(); } } @@ -1250,9 +1274,11 @@ bool SelectionSingleton::isSelected(App::DocumentObject* pObject, const char* pS if (sel.DocName==pDocName && sel.FeatName==pObjectName) { if(!pSubName || sel.SubName==pSubName) return true; + if(resolve>1 && boost::starts_with(sel.SubName,prefix)) + return true; } } - if(resolve) { + if(resolve==1) { for(auto &sel : _SelList) { if(sel.pResolvedObject != pResolvedObject) continue; @@ -1497,49 +1523,18 @@ PyObject *SelectionSingleton::sUpdateSelection(PyObject * /*self*/, PyObject *ar PyObject *show; PyObject *object; char* subname=0; - if (PyArg_ParseTuple(args, "OO!|s", &show,&(App::DocumentObjectPy::Type),&object,&subname)) { - App::DocumentObjectPy* docObjPy = static_cast(object); - App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); - if (!docObj || !docObj->getNameInDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check invalid object"); - return NULL; - } - - Selection().updateSelection(PyObject_IsTrue(show), - docObj->getDocument()->getName(), docObj->getNameInDocument(), subname); - Py_Return; - } - - PyErr_Clear(); - PyObject *sequence; - if (PyArg_ParseTuple(args, "O!O", &(App::DocumentObjectPy::Type),&object,&sequence)) { - App::DocumentObjectPy* docObjPy = static_cast(object); - App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); - if (!docObj || !docObj->getNameInDocument()) { - PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check invalid object"); - return NULL; - } - - try { - if (PyTuple_Check(sequence) || PyList_Check(sequence)) { - Py::Sequence list(sequence); - for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { - std::string subname = static_cast(Py::String(*it)); - Selection().updateSelection(docObj->getDocument()->getName(), - docObj->getNameInDocument(), - subname.c_str()); - } - - Py_Return; - } - } - catch (const Py::Exception&) { - // do nothing here - } + if(!PyArg_ParseTuple(args, "OO!|s", &show,&(App::DocumentObjectPy::Type),&object,&subname)) + return 0; + App::DocumentObjectPy* docObjPy = static_cast(object); + App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); + if (!docObj || !docObj->getNameInDocument()) { + PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check invalid object"); + return NULL; } - PyErr_SetString(PyExc_ValueError, "type must be 'DocumentObject[,subname[,x,y,z]]' or 'DocumentObject, list or tuple of subnames'"); - return 0; + Selection().updateSelection(PyObject_IsTrue(show), + docObj->getDocument()->getName(), docObj->getNameInDocument(), subname); + Py_Return; } diff --git a/src/Gui/Selection.h b/src/Gui/Selection.h index bd9f4b8792dd..e6f6b2169c4d 100644 --- a/src/Gui/Selection.h +++ b/src/Gui/Selection.h @@ -307,9 +307,9 @@ class GuiExport SelectionSingleton : public Base::Subjectunref(); currenthighlight = 0; + Selection().rmvPreselect(); } this->touch(); } diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 6c3cf83c5b56..3bad6b9deb81 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -71,11 +71,16 @@ FC_LOG_LEVEL_INIT("Tree",false,true,true); using namespace Gui; +#define TREEVIEW_PARAM "User parameter:BaseApp/Preferences/TreeView" +#define GET_TREEVIEW_PARAM(_name) \ + ParameterGrp::handle _name = App::GetApplication().GetParameterGroupByPath(TREEVIEW_PARAM) + +///////////////////////////////////////////////////////////////////////////////// + QPixmap* TreeWidget::documentPixmap = 0; const int TreeWidget::DocumentType = 1000; const int TreeWidget::ObjectType = 1001; - /* TRANSLATOR Gui::TreeWidget */ TreeWidget::TreeWidget(const char *name, QWidget* parent) : QTreeWidget(parent), SelectionObserver(false,false), contextItem(0) @@ -87,17 +92,19 @@ TreeWidget::TreeWidget(const char *name, QWidget* parent) this->setDropIndicatorShown(false); this->setRootIsDecorated(false); -#define GET_TREEVIEW_PARAM(_name) \ - ParameterGrp::handle _name = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView") - GET_TREEVIEW_PARAM(hGrp); - bool sync = hGrp->GetBool("SyncSelection",true); this->syncSelectionAction = new QAction(this); this->syncSelectionAction->setCheckable(true); - this->syncSelectionAction->setChecked(sync); + this->syncSelectionAction->setChecked(hGrp->GetBool("SyncSelection",true)); connect(this->syncSelectionAction, SIGNAL(triggered()), this, SLOT(onSyncSelection())); + this->preSelectionAction = new QAction(this); + this->preSelectionAction->setCheckable(true); + this->preSelectionAction->setChecked(hGrp->GetBool("PreSelection",true)); + connect(this->preSelectionAction, SIGNAL(triggered()), + this, SLOT(onPreSelection())); + this->syncViewAction = new QAction(this); this->syncViewAction->setCheckable(true); this->syncViewAction->setChecked(hGrp->GetBool("SyncView",false)); @@ -168,6 +175,9 @@ TreeWidget::TreeWidget(const char *name, QWidget* parent) this->setMouseTracking(true); // needed for itemEntered() to work #endif + this->preselectTimer = new QTimer(this); + this->preselectTimer->setSingleShot(true); + this->statusTimer = new QTimer(this); this->statusTimer->setSingleShot(false); @@ -181,6 +191,9 @@ TreeWidget::TreeWidget(const char *name, QWidget* parent) this, SLOT(onItemExpanded(QTreeWidgetItem*))); connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(onItemSelectionChanged())); + connect(this->preselectTimer, SIGNAL(timeout()), + this, SLOT(onPreSelectTimer())); + preselectTime.start(); setupText(); _updateStatus(); @@ -232,8 +245,12 @@ void TreeWidget::contextMenuEvent (QContextMenuEvent * e) contextMenu.addSeparator(); topact = actions.front(); } - contextMenu.insertAction(topact,this->syncSelectionAction); - contextMenu.insertAction(topact,this->syncViewAction); + QMenu optionsMenu; + optionsMenu.setTitle(tr("Tree view options")); + optionsMenu.addAction(this->preSelectionAction); + optionsMenu.addAction(this->syncSelectionAction); + optionsMenu.addAction(this->syncViewAction); + contextMenu.insertMenu(topact,&optionsMenu); contextMenu.insertSeparator(topact); // get the current item @@ -1101,11 +1118,51 @@ void TreeWidget::onItemEntered(QTreeWidgetItem * item) { // object item selected if (item && item->type() == TreeWidget::ObjectType) { - DocumentObjectItem* obj = static_cast(item); - obj->displayStatusInfo(); + DocumentObjectItem* objItem = static_cast(item); + objItem->displayStatusInfo(); + + if(preSelectionAction->isChecked()) { + if(preselectTime.elapsed() < 700) + onPreSelectTimer(); + else{ + preselectTimer->start(500); + Selection().rmvPreselect(); + } + } + } else if(preSelectionAction->isChecked()) + Selection().rmvPreselect(); +} + +void TreeWidget::leaveEvent(QEvent *) { + if(preSelectionAction->isChecked()) { + preselectTimer->stop(); + Selection().rmvPreselect(); } } +void TreeWidget::onPreSelectTimer() { + if(!preSelectionAction->isChecked()) + return; + auto item = itemAt(viewport()->mapFromGlobal(QCursor::pos())); + if(!item || item->type()!=TreeWidget::ObjectType) + return; + + FC_LOG("preselect timer"); + preselectTime.restart(); + DocumentObjectItem* objItem = static_cast(item); + auto vp = objItem->object(); + auto obj = vp->getObject(); + std::ostringstream ss; + App::DocumentObject *parent = 0; + objItem->getSubName(ss,parent); + if(parent) + ss << obj->getNameInDocument() << '.'; + else + parent = obj; + Selection().setPreselect(parent->getDocument()->getName(),parent->getNameInDocument(), + ss.str().c_str(),0,0,0,true); +} + void TreeWidget::onItemCollapsed(QTreeWidgetItem * item) { // object item collapsed @@ -1142,6 +1199,9 @@ void TreeWidget::setupText() { this->headerItem()->setText(0, tr("Labels & Attributes")); this->rootItem->setText(0, tr("Application")); + this->preSelectionAction->setText(tr("Pre-selection")); + this->preSelectionAction->setStatusTip(tr("Preselect the object in 3D view when mouse over the tree item")); + this->syncSelectionAction->setText(tr("Sync selection")); this->syncSelectionAction->setStatusTip(tr("Auto expand item when selected in 3D view")); @@ -1175,6 +1235,12 @@ void TreeWidget::onSyncSelection() { hGrp->SetBool("SyncSelection",syncSelectionAction->isChecked()); } +void TreeWidget::onPreSelection() { + GET_TREEVIEW_PARAM(hGrp); + hGrp->SetBool("PreSelection",preSelectionAction->isChecked()); +} + + void TreeWidget::onSyncView() { GET_TREEVIEW_PARAM(hGrp); hGrp->SetBool("SyncView",syncViewAction->isChecked()); @@ -1287,7 +1353,7 @@ TreeDockWidget::TreeDockWidget(Gui::Document* pcDocument,QWidget *parent) setWindowTitle(tr("Tree view")); this->treeWidget = new TreeWidget("TreeView",this); this->treeWidget->setRootIsDecorated(false); - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); + GET_TREEVIEW_PARAM(hGrp); this->treeWidget->setIndentation(hGrp->GetInt("Indentation", this->treeWidget->indentation())); QGridLayout* pLayout = new QGridLayout(this); diff --git a/src/Gui/Tree.h b/src/Gui/Tree.h index bc0dbc1e9cb4..229a06b5821a 100644 --- a/src/Gui/Tree.h +++ b/src/Gui/Tree.h @@ -25,6 +25,7 @@ #define GUI_TREE_H #include +#include #include #include @@ -119,6 +120,7 @@ class TreeWidget : public QTreeWidget, public SelectionObserver protected: void showEvent(QShowEvent *) override; void hideEvent(QHideEvent *) override; + void leaveEvent(QEvent *) override; void _updateStatus(bool delay=false); protected Q_SLOTS: @@ -130,6 +132,8 @@ protected Q_SLOTS: void onSkipRecompute(bool on); void onMarkRecompute(); void onSyncSelection(); + void onPreSelection(); + void onPreSelectTimer(); void onSyncView(); void onShowHidden(); void onHideInTree(); @@ -159,6 +163,7 @@ private Q_SLOTS: QAction* finishEditingAction; QAction* skipRecomputeAction; QAction* markRecomputeAction; + QAction* preSelectionAction; QAction* syncSelectionAction; QAction* syncViewAction; QAction* showHiddenAction; @@ -168,6 +173,8 @@ private Q_SLOTS: DocumentItem *currentDocItem; QTreeWidgetItem* rootItem; QTimer* statusTimer; + QTimer* preselectTimer; + QTime preselectTime; static QPixmap* documentPixmap; std::map DocumentMap; bool fromOutside; diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index fca248f32251..24a8bea9230a 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -75,6 +75,7 @@ # include # include # include +# include # include # include # include @@ -472,11 +473,25 @@ void View3DInventorViewer::init() pcGroupOnTop->ref(); pcViewProviderRoot->addChild(pcGroupOnTop); - pcGroupOnTopPickStyle = new SoPickStyle; - pcGroupOnTopPickStyle->ref(); + auto pcGroupOnTopPickStyle = new SoPickStyle; pcGroupOnTopPickStyle->style = SoPickStyle::UNPICKABLE; // pcGroupOnTopPickStyle->style = SoPickStyle::SHAPE_ON_TOP; pcGroupOnTopPickStyle->setOverride(true); + pcGroupOnTop->addChild(pcGroupOnTopPickStyle); + + coin_setenv("COIN_SEPARATE_DIFFUSE_TRANSPARENCY_OVERRIDE", "1", TRUE); + auto pcOnTopMaterial = new SoMaterial; + pcOnTopMaterial->transparency = 0.5; + pcOnTopMaterial->diffuseColor.setIgnored(true); + pcOnTopMaterial->setOverride(true); + pcGroupOnTop->addChild(pcOnTopMaterial); + + pcGroupOnTopSel = new SoGroup; + pcGroupOnTopSel->ref(); + pcGroupOnTop->addChild(pcGroupOnTopSel); + pcGroupOnTopPreSel = new SoGroup; + pcGroupOnTopPreSel->ref(); + pcGroupOnTop->addChild(pcGroupOnTopPreSel); pcEditingRoot = new SoSeparator; pcEditingRoot->ref(); @@ -578,6 +593,8 @@ View3DInventorViewer::~View3DInventorViewer() this->backlight = 0; this->pcGroupOnTop->unref(); + this->pcGroupOnTopPreSel->unref(); + this->pcGroupOnTopSel->unref(); this->pcEditingRoot->unref(); this->pcEditingTransform->unref(); @@ -621,18 +638,55 @@ void View3DInventorViewer::initialize() } void View3DInventorViewer::clearGroupOnTop() { - if(!pcGroupOnTop->getNumChildren()) - return; - + objectsOnTop.clear(); + objectsOnTopPreSel.clear(); SoSelectionElementAction action(SoSelectionElementAction::None,true); - action.apply(pcGroupOnTop); - pcGroupOnTop->removeAllChildren(); + action.apply(pcGroupOnTopSel); + action.apply(pcGroupOnTopPreSel); + pcGroupOnTopSel->removeAllChildren(); + pcGroupOnTopPreSel->removeAllChildren(); FC_LOG("clear annoation"); } -void View3DInventorViewer::addToGroupOnTop(App::DocumentObject *obj, const char *subname) { +void View3DInventorViewer::checkGroupOnTop(const SelectionChanges &Reason) { + if(Reason.Type == SelectionChanges::SetSelection || Reason.Type == SelectionChanges::ClrSelection) { + clearGroupOnTop(); + if(Reason.Type == SelectionChanges::ClrSelection) + return; + } + + if(!getDocument() || !Reason.pDocName || !Reason.pDocName[0] || !Reason.pObjectName) + return; + auto obj = getDocument()->getDocument()->getObject(Reason.pObjectName); if(!obj || !obj->getNameInDocument()) return; + std::string key(obj->getNameInDocument()); + key += '.'; + auto subname = Reason.pSubName; + if(subname) + key += subname; + if(Reason.Type == SelectionChanges::RmvSelection || Reason.Type == SelectionChanges::RmvPreselect) { + auto &objs = Reason.Type==SelectionChanges::RmvSelection?objectsOnTop:objectsOnTopPreSel; + auto pcGroup = Reason.Type==SelectionChanges::RmvSelection?pcGroupOnTopSel:pcGroupOnTopPreSel; + auto it = objs.find(key.c_str()); + if(it == objs.end()) + return; + int index = pcGroup->findChild(it->second); + if(index >= 0) { + SoSelectionElementAction action(SoSelectionElementAction::None,true); + action.apply(it->second); + pcGroup->removeChild(index); + } + FC_LOG("remove annoation " << Reason.Type << " " << key); + objs.erase(it); + return; + } + + auto &objs = Reason.Type==SelectionChanges::SetPreselect?objectsOnTopPreSel:objectsOnTop; + auto pcGroup = Reason.Type==SelectionChanges::SetPreselect?pcGroupOnTopPreSel:pcGroupOnTopSel; + + if(objs.find(key.c_str())!=objs.end()) + return; auto vp = dynamic_cast( Application::Instance->getViewProvider(obj)); if(!vp || !vp->isSelectable() || !vp->isShow()) @@ -649,12 +703,14 @@ void View3DInventorViewer::addToGroupOnTop(App::DocumentObject *obj, const char return; } } - int onTop = svp->OnTopWhenSelected.getValue(); - if(!onTop) - return; + int onTop; // onTop==2 means on top only if whole object is selected, // onTop==3 means on top only if some sub-element is selected // onTop==1 means either + if(Reason.Type == SelectionChanges::SetPreselect) + onTop = 2; + else if(!(onTop=svp->OnTopWhenSelected.getValue())) + return; if(onTop==2 || onTop==3) { if(subname && *subname) { size_t len = strlen(subname); @@ -694,8 +750,6 @@ void View3DInventorViewer::addToGroupOnTop(App::DocumentObject *obj, const char groups.push_back(grpVp); } - FC_LOG("add annoation " << obj->getNameInDocument() << '.' << (subname?subname:"")); - SoPath *path = new SoPath(10); path->ref(); @@ -710,7 +764,7 @@ void View3DInventorViewer::addToGroupOnTop(App::DocumentObject *obj, const char if(vp->getDetailPath(subname, static_cast(path),true,det) && path->getLength()) { auto node = new SoFCPathAnnotation; node->setPath(path); - pcGroupOnTop->addChild(node); + pcGroup->addChild(node); if(det) { SoSelectionElementAction action(SoSelectionElementAction::Append,true); action.setElement(det); @@ -722,6 +776,8 @@ void View3DInventorViewer::addToGroupOnTop(App::DocumentObject *obj, const char action.apply(&tmpPath); tmpPath.unrefNoDelete(); } + FC_LOG("add annoation " << Reason.Type << " " << key); + objs[key.c_str()] = node; } delete det; path->unref(); @@ -744,31 +800,27 @@ void View3DInventorViewer::onSelectionChanged(const SelectionChanges &_Reason) else Reason.Type = SelectionChanges::RmvSelection; // fall through + case SelectionChanges::SetPreselect: + case SelectionChanges::RmvPreselect: case SelectionChanges::SetSelection: case SelectionChanges::AddSelection: - case SelectionChanges::RmvSelection: { - if(!Reason.pDocName) - return; - clearGroupOnTop(); - pcGroupOnTop->addChild(pcGroupOnTopPickStyle); - const auto &sels = Selection().getSelection(Reason.pDocName,false); - for(auto it = sels.rbegin();it!=sels.rend();++it) - addToGroupOnTop(it->pObject,it->SubName); - break; - } case SelectionChanges::ClrSelection:{ - clearGroupOnTop(); + case SelectionChanges::RmvSelection: + case SelectionChanges::ClrSelection: + checkGroupOnTop(Reason); break; - } case SelectionChanges::SetPreselectSignal: + case SelectionChanges::SetPreselectSignal: break; - case SelectionChanges::RmvPreselect: { + default: + return; + } + + if(Reason.Type == SelectionChanges::RmvPreselect) { SoFCHighlightAction cAct(Reason); cAct.apply(pcViewProviderRoot); - break; - } default: - return; + } else { + SoFCSelectionAction cAct(Reason); + cAct.apply(pcViewProviderRoot); } - SoFCSelectionAction cAct(Reason); - cAct.apply(pcViewProviderRoot); } /// @endcond diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index 603bb771c677..ebcfe9952151 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -131,7 +131,7 @@ class GuiExport View3DInventorViewer : public Quarter::SoQTQuarterAdaptor, publi /// Observer message from the Selection virtual void onSelectionChanged(const SelectionChanges &Reason); - void addToGroupOnTop(App::DocumentObject *obj, const char *subname); + void checkGroupOnTop(const SelectionChanges &Reason); void clearGroupOnTop(); SoDirectionalLight* getBacklight(void) const; @@ -418,8 +418,13 @@ class GuiExport View3DInventorViewer : public Quarter::SoQTQuarterAdaptor, publi SoDirectionalLight* backlight; SoSeparator * pcViewProviderRoot; + SoSeparator * pcGroupOnTop; - SoPickStyle *pcGroupOnTopPickStyle; + SoGroup * pcGroupOnTopSel; + SoGroup * pcGroupOnTopPreSel; + std::map objectsOnTop; + std::map objectsOnTopPreSel; + SoSeparator * pcEditingRoot; SoTransform * pcEditingTransform; bool restoreEditingRoot; diff --git a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp index 1cf6423561c1..390363dfee84 100644 --- a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp +++ b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp @@ -110,14 +110,35 @@ void SoBrepEdgeSet::GLRender(SoGLRenderAction *action) if(ctx2 && ctx2->selectionIndex.empty()) return; + if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + }else{ + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + if(action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + return; + } + if(!action->isRenderingDelayedPaths()) renderHighlight(action,ctx); if(ctx && ctx->selectionIndex.size()) { if(ctx->isSelectAll()) { - if(ctx2 && ctx2->selectionIndex.size()) { + if(ctx2) { ctx2->selectionColor = ctx->selectionColor; renderSelection(action,ctx2); - }else + }else if(ctx->isSelectAll()) renderSelection(action,ctx); if(action->isRenderingDelayedPaths()) renderHighlight(action,ctx); @@ -200,12 +221,17 @@ void SoBrepEdgeSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) int num = (int)ctx->hl.size(); if (num > 0) { - const int32_t* id = &(ctx->hl[0]); - if (!validIndexes(coords, ctx->hl)) { - SoDebugError::postWarning("SoBrepEdgeSet::renderHighlight", "highlightIndex out of range"); + if (ctx->hl[0] < 0) { + renderShape(static_cast(coords), cindices, numcindices); } else { - renderShape(static_cast(coords), id, num); + const int32_t* id = &(ctx->hl[0]); + if (!validIndexes(coords, ctx->hl)) { + SoDebugError::postWarning("SoBrepEdgeSet::renderHighlight", "highlightIndex out of range"); + } + else { + renderShape(static_cast(coords), id, num); + } } } state->pop(); @@ -279,7 +305,13 @@ void SoBrepEdgeSet::doAction(SoAction* action) return; } const SoDetail* detail = hlaction->getElement(); - if (detail) { + if (!detail) { + ctx->highlightColor = hlaction->getColor(); + ctx->highlightIndex = INT_MAX; + ctx->hl.clear(); + ctx->hl.push_back(-1); + touch(); + } else { if (!detail->isOfType(SoLineDetail::getClassTypeId())) { ctx->highlightIndex = -1; ctx->hl.clear(); diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.cpp b/src/Mod/Part/Gui/SoBrepFaceSet.cpp index fb75c28d8d24..7e246c08be18 100644 --- a/src/Mod/Part/Gui/SoBrepFaceSet.cpp +++ b/src/Mod/Part/Gui/SoBrepFaceSet.cpp @@ -112,6 +112,10 @@ class SoBrepFaceSet::SelContext { bool isSelectAll() const { return selectionIndex.size() && *selectionIndex.begin()<0; } + + bool isHighlightAll() const { + return highlightIndex==INT_MAX && (selectionIndex.empty() || isSelectAll()); + } }; #define PRIVATE(p) ((p)->pimpl) @@ -227,16 +231,19 @@ void SoBrepFaceSet::doAction(SoAction* action) } const SoDetail* detail = hlaction->getElement(); - if (detail) { + if (!detail) { + ctx->highlightIndex = INT_MAX; + ctx->highlightColor = hlaction->getColor(); + }else { if (detail->isOfType(SoFaceDetail::getClassTypeId())) { int index = static_cast(detail)->getPartIndex(); ctx->highlightIndex = index; ctx->highlightColor = hlaction->getColor(); } else ctx->highlightIndex = -1; - touch(); - return; } + touch(); + return; } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { Gui::SoSelectionElementAction* selaction = static_cast(action); @@ -516,13 +523,52 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action) // override material binding to PER_PART_INDEX to achieve // preselection/selection with transparency - bool pushed = overrideMaterialBinding(state,ctx,ctx2); + bool pushed = overrideMaterialBinding(action,ctx,ctx2); if(!pushed){ // for non transparent cases, we still use the old selection rendering // code, because it can override emission color, which gives a more // distinguishable selection highlight. The above material binding // override method can't, because Coin does not support per part // emission color + + // There are a few factors affects the rendering order. + // + // 1) For normal case, the highlight (pre-selection) is the top layer. And since + // the depth buffer clipping is on here, we shall draw highlight first, then + // selection, then the rest part. + // + // 2) If action->isRenderingDelayedPaths() is true, it means we are rendering + // with depth buffer clipping turned off (always on top rendering), so we shall + // draw the top layer last, i.e. renderHighlight() last + // + // 3) If highlightIndex==INT_MAX, it means we are rendering full object highlight + // In order to not obscure selection layer, we shall draw highlight after selection + // if and only if it is not a full object selection. + // + // Transparency complicates stuff even more, but not here. It will be handled inside + // overrideMaterialBinding() + // + if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + }else{ + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + if(action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + return; + } + if(!action->isRenderingDelayedPaths()) renderHighlight(action,ctx); if(ctx && ctx->selectionIndex.size()) { @@ -559,7 +605,6 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action) // correctly. if(this->shouldGLRender(action)) { SbBool hasVBO = PRIVATE(this)->vboAvailable; - SoState * state = action->getState(); if (hasVBO) { // get the VBO status of the viewer Gui::SoGLVBOActivatedElement::get(state, hasVBO); @@ -626,9 +671,10 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action) } #endif -bool SoBrepFaceSet::overrideMaterialBinding(SoState *state, SelContextPtr ctx, SelContextPtr ctx2) { +bool SoBrepFaceSet::overrideMaterialBinding(SoGLRenderAction *action, SelContextPtr ctx, SelContextPtr ctx2) { if(!ctx && !ctx2) return false; + auto state = action->getState(); auto mb = SoMaterialBindingElement::get(state); auto element = SoLazyElement::getInstance(state); @@ -662,6 +708,15 @@ bool SoBrepFaceSet::overrideMaterialBinding(SoState *state, SelContextPtr ctx, S { state->push(); + if(action->isRenderingDelayedPaths()) { + // rendering delayed paths means we are doing annotation (e.g. + // always on top rendering). To render transparency correctly in + // this case, we shall use openGL transparency blend. Override + // using SoLazyElement::setTransparencyType() doesn't seem to work + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + } packedColors.clear(); if(ctx && Gui::Selection().needPickedList()) { @@ -669,11 +724,15 @@ bool SoBrepFaceSet::overrideMaterialBinding(SoState *state, SelContextPtr ctx, S // trans_size = 1; } - if(!ctx2 && ctx && ctx->highlightIndex<0 && ctx->isSelectAll()) { - //optimization for full selection without highlight or partial rendering + auto selColor = ctx->highlightIndex==INT_MAX?ctx->highlightColor:ctx->selectionColor; + + if(!ctx2 && ctx && + (ctx->isHighlightAll() || (ctx->highlightIndex<0 && ctx->isSelectAll()))) + { + //optimization for full selection/highlight without partial rendering SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL); SoOverrideElement::setMaterialBindingOverride(state, this, true); - packedColors.push_back(ctx->selectionColor.getPackedValue(trans0)); + packedColors.push_back(selColor.getPackedValue(trans0)); SoLazyElement::setPacked(state, this,1, &packedColors[0],true); SoTextureEnabledElement::set(state,this,false); return true; @@ -682,14 +741,14 @@ bool SoBrepFaceSet::overrideMaterialBinding(SoState *state, SelContextPtr ctx, S matIndex.clear(); matIndex.reserve(partIndex.getNum()); - if(ctx && ctx->isSelectAll()) { + if(ctx && (ctx->isSelectAll() || ctx->isHighlightAll())) { matIndex.resize(partIndex.getNum(),0); if(!ctx2) - packedColors.push_back(ctx->selectionColor.getPackedValue(trans0)); + packedColors.push_back(selColor.getPackedValue(trans0)); else { // default to full transparent - packedColors.push_back(ctx->selectionColor.getPackedValue(1.0)); - packedColors.push_back(ctx->selectionColor.getPackedValue(trans0)); + packedColors.push_back(selColor.getPackedValue(1.0)); + packedColors.push_back(selColor.getPackedValue(trans0)); for(auto idx : ctx2->selectionIndex) { if(idx>=0 && idxhighlightIndex] = packedColors.size()-1; } }else{ + auto diffuseColor = ctx->highlightIndex==INT_MAX?ctx->highlightColor:diffuse[0]; + if(ctx2) {// for partial rendering - packedColors.push_back(diffuse[0].getPackedValue(1.0)); + packedColors.push_back(diffuseColor.getPackedValue(1.0)); matIndex.resize(partIndex.getNum(),0); - if(mb == SoMaterialBindingElement::OVERALL) { - packedColors.push_back(diffuse[0].getPackedValue(trans0)); + if(mb == SoMaterialBindingElement::OVERALL || ctx->highlightIndex==INT_MAX) { + packedColors.push_back(diffuseColor.getPackedValue(trans0)); for(auto idx : ctx2->selectionIndex) { if(idx>=0 && idxhighlightIndex==INT_MAX) { + packedColors.push_back(diffuseColor.getPackedValue(trans0)); matIndex.resize(partIndex.getNum(),0); }else{ assert(diffuse_size >= partIndex.getNum()); @@ -1073,8 +1134,8 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) mb.sendFirst(); // make sure we have the correct material - int32_t id = ctx->highlightIndex; - if (id >= this->partIndex.getNum()) { + int id = ctx->highlightIndex; + if (id!=INT_MAX && id >= this->partIndex.getNum()) { SoDebugError::postWarning("SoBrepFaceSet::renderHighlight", "highlightIndex out of range"); } else { @@ -1084,11 +1145,17 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) pindices = this->partIndex.getValues(0); // coords - int length = (int)pindices[id]*4; int start=0; - for (int i=0;iselectionIndex.empty()) return; + if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + }else{ + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + if(action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + return; + } + if(!action->isRenderingDelayedPaths()) renderHighlight(action,ctx); if(ctx && ctx->selectionIndex.size()) { @@ -172,8 +193,12 @@ void SoBrepPointSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material - int32_t id = ctx->highlightIndex; - if (id < this->startIndex.getValue() || id >= coords->getNum()) { + int id = ctx->highlightIndex; + if(id == INT_MAX) { + const SbVec3f * coords3d = coords->getArrayPtr3(); + for(int idx=startIndex.getValue();idxgetNum();++idx) + glVertex3fv((const GLfloat*) (coords3d + idx)); + }else if (id < this->startIndex.getValue() || id >= coords->getNum()) { SoDebugError::postWarning("SoBrepPointSet::renderHighlight", "highlightIndex out of range"); } else { @@ -242,19 +267,22 @@ void SoBrepPointSet::doAction(SoAction* action) return; } const SoDetail* detail = hlaction->getElement(); - if (detail) { - if (!detail->isOfType(SoPointDetail::getClassTypeId())) { - ctx->highlightIndex = -1; - touch(); - return; - } + if (!detail) { + ctx->highlightIndex = INT_MAX; + ctx->highlightColor = hlaction->getColor(); + touch(); + return; + }else if (!detail->isOfType(SoPointDetail::getClassTypeId())) { + ctx->highlightIndex = -1; + touch(); + return; + } - int index = static_cast(detail)->getCoordinateIndex(); - if(index!=ctx->highlightIndex) { - ctx->highlightIndex = index; - ctx->highlightColor = hlaction->getColor(); - touch(); - } + int index = static_cast(detail)->getCoordinateIndex(); + if(index!=ctx->highlightIndex) { + ctx->highlightIndex = index; + ctx->highlightColor = hlaction->getColor(); + touch(); } return; }