Gui: Add a context menu to select obstructed items (from RT fork)
As the title says, this is from RT's fork. I only adjusted it a little bit and removed pie selection which was previously in the implementation. To activate context menu - "G, G", then if it can't resolve edges vs faces, it adds QMenus on top to let user decide which one to select. Co-authored-by: tetektoza <tetektoza@users.noreply.github.com>
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
# include <Inventor/events/SoMouseButtonEvent.h>
|
||||
# include <Inventor/nodes/SoOrthographicCamera.h>
|
||||
# include <Inventor/nodes/SoPerspectiveCamera.h>
|
||||
# include <Inventor/SoPickedPoint.h>
|
||||
# include <QApplication>
|
||||
# include <QDialog>
|
||||
# include <QDomDocument>
|
||||
@@ -47,6 +48,7 @@
|
||||
#include <App/Document.h>
|
||||
#include <App/DocumentObject.h>
|
||||
#include <App/DocumentObjectGroup.h>
|
||||
#include <App/DocumentObserver.h>
|
||||
#include <App/GeoFeature.h>
|
||||
#include <App/GeoFeatureGroupExtension.h>
|
||||
#include <App/Part.h>
|
||||
@@ -73,6 +75,7 @@
|
||||
#include "OverlayManager.h"
|
||||
#include "SceneInspector.h"
|
||||
#include "Selection.h"
|
||||
#include "Selection/SelectionView.h"
|
||||
#include "SelectionObject.h"
|
||||
#include "SoFCOffscreenRenderer.h"
|
||||
#include "TextureMapping.h"
|
||||
@@ -3953,6 +3956,114 @@ bool StdCmdAlignToSelection::isActive()
|
||||
return getGuiApplication()->sendHasMsgToActiveView("AlignToSelection");
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Std_PickGeometry
|
||||
//===========================================================================
|
||||
|
||||
DEF_STD_CMD_A(StdCmdPickGeometry)
|
||||
|
||||
StdCmdPickGeometry::StdCmdPickGeometry()
|
||||
: Command("Std_PickGeometry")
|
||||
{
|
||||
sGroup = "View";
|
||||
sMenuText = QT_TR_NOOP("Pick geometry");
|
||||
sToolTipText = QT_TR_NOOP("Pick hidden geometries under the mouse cursor in 3D view.\n"
|
||||
"This command is supposed to be activated by keyboard shortcut.");
|
||||
sWhatsThis = "Std_PickGeometry";
|
||||
sStatusTip = sToolTipText;
|
||||
sAccel = "G, G";
|
||||
eType = NoTransaction | AlterSelection;
|
||||
}
|
||||
|
||||
void StdCmdPickGeometry::activated(int iMsg)
|
||||
{
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
// Get the active view
|
||||
auto view = Application::Instance->activeView();
|
||||
if (!view || !view->isDerivedFrom(View3DInventor::getClassTypeId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto view3d = static_cast<View3DInventor*>(view);
|
||||
auto viewer = view3d->getViewer();
|
||||
if (!viewer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get cursor position in the viewer
|
||||
QPoint pos = QCursor::pos();
|
||||
QWidget* widget = viewer->getGLWidget();
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
QPoint local = widget->mapFromGlobal(pos);
|
||||
SbVec2s point;
|
||||
point[0] = local.x();
|
||||
point[1] = widget->height() - local.y() - 1;
|
||||
|
||||
// Use ray picking to get all objects under cursor
|
||||
SoRayPickAction pickAction(viewer->getSoRenderManager()->getViewportRegion());
|
||||
pickAction.setPoint(point);
|
||||
pickAction.setRadius(viewer->getPickRadius());
|
||||
pickAction.setPickAll(true); // Get all objects under cursor
|
||||
pickAction.apply(viewer->getSoRenderManager()->getSceneGraph());
|
||||
|
||||
const SoPickedPointList& pplist = pickAction.getPickedPointList();
|
||||
if (pplist.getLength() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert picked points to PickData list
|
||||
std::vector<PickData> selections;
|
||||
|
||||
for (int i = 0; i < pplist.getLength(); ++i) {
|
||||
SoPickedPoint* pp = pplist[i];
|
||||
if (!pp || !pp->getPath())
|
||||
continue;
|
||||
|
||||
ViewProvider* vp = viewer->getViewProviderByPath(pp->getPath());
|
||||
if (!vp)
|
||||
continue;
|
||||
|
||||
// Cast to ViewProviderDocumentObject to get the object
|
||||
auto vpDoc = dynamic_cast<Gui::ViewProviderDocumentObject*>(vp);
|
||||
if (!vpDoc)
|
||||
continue;
|
||||
|
||||
App::DocumentObject* obj = vpDoc->getObject();
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
std::string elementName = vp->getElement(pp->getDetail());
|
||||
|
||||
// Create PickData with selection information
|
||||
PickData pickData;
|
||||
pickData.obj = obj;
|
||||
pickData.element = elementName;
|
||||
pickData.docName = obj->getDocument()->getName();
|
||||
pickData.objName = obj->getNameInDocument();
|
||||
pickData.subName = elementName;
|
||||
|
||||
selections.push_back(pickData);
|
||||
}
|
||||
|
||||
if (selections.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use SelectionMenu to display and handle the pick menu
|
||||
SelectionMenu contextMenu(widget);
|
||||
contextMenu.doPick(selections);
|
||||
}
|
||||
|
||||
bool StdCmdPickGeometry::isActive()
|
||||
{
|
||||
auto view = qobject_cast<View3DInventor*>(getMainWindow()->activeWindow());
|
||||
return view != nullptr;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Instantiation
|
||||
//===========================================================================
|
||||
@@ -3985,6 +4096,7 @@ void CreateViewStdCommands()
|
||||
rcCmdMgr.addCommand(new StdRecallWorkingView());
|
||||
rcCmdMgr.addCommand(new StdCmdViewGroup());
|
||||
rcCmdMgr.addCommand(new StdCmdAlignToSelection());
|
||||
rcCmdMgr.addCommand(new StdCmdPickGeometry());
|
||||
|
||||
rcCmdMgr.addCommand(new StdCmdViewExample1());
|
||||
rcCmdMgr.addCommand(new StdCmdViewExample2());
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
#include "Navigation/NavigationStyle.h"
|
||||
#include "Navigation/NavigationStylePy.h"
|
||||
#include "Application.h"
|
||||
#include "Command.h"
|
||||
#include "Action.h"
|
||||
#include "Inventor/SoMouseWheelEvent.h"
|
||||
#include "MenuManager.h"
|
||||
#include "MouseSelection.h"
|
||||
@@ -1953,6 +1955,8 @@ void NavigationStyle::openPopupMenu(const SbVec2s& position)
|
||||
QAction *item = navMenuGroup->addAction(name);
|
||||
navMenu->addAction(item);
|
||||
item->setCheckable(true);
|
||||
QByteArray data(style.first.getName());
|
||||
item->setData(data);
|
||||
|
||||
if (const Base::Type item_style = style.first; item_style != this->getTypeId()) {
|
||||
auto triggeredFun = [this, item_style](){
|
||||
@@ -1970,7 +1974,64 @@ void NavigationStyle::openPopupMenu(const SbVec2s& position)
|
||||
item->setChecked(true);
|
||||
}
|
||||
|
||||
contextMenu->popup(QCursor::pos());
|
||||
// Add Pick Geometry option if there are objects under cursor
|
||||
bool separator = false;
|
||||
auto posAction = !contextMenu->actions().empty() ? contextMenu->actions().front() : nullptr;
|
||||
|
||||
// Get picked objects at position
|
||||
SoRayPickAction rp(viewer->getSoRenderManager()->getViewportRegion());
|
||||
rp.setPoint(position);
|
||||
rp.setRadius(viewer->getPickRadius());
|
||||
rp.setPickAll(true);
|
||||
rp.apply(viewer->getSoRenderManager()->getSceneGraph());
|
||||
|
||||
const SoPickedPointList& pplist = rp.getPickedPointList();
|
||||
QAction *pickAction = nullptr;
|
||||
|
||||
if (pplist.getLength() > 0) {
|
||||
separator = true;
|
||||
auto cmd = Application::Instance->commandManager().getCommandByName("Std_PickGeometry");
|
||||
if (cmd) {
|
||||
pickAction = new QAction(cmd->getAction()->text(), contextMenu);
|
||||
pickAction->setShortcut(cmd->getAction()->shortcut());
|
||||
} else {
|
||||
pickAction = new QAction(QObject::tr("Pick geometry"), contextMenu);
|
||||
}
|
||||
if (posAction) {
|
||||
contextMenu->insertAction(posAction, pickAction);
|
||||
contextMenu->insertSeparator(posAction);
|
||||
} else {
|
||||
contextMenu->addAction(pickAction);
|
||||
}
|
||||
}
|
||||
|
||||
if (separator && posAction)
|
||||
contextMenu->insertSeparator(posAction);
|
||||
|
||||
QAction* used = contextMenu->exec(QCursor::pos());
|
||||
if (used && navMenuGroup->actions().indexOf(used) >= 0 && used->isChecked()) {
|
||||
QByteArray type = used->data().toByteArray();
|
||||
QWidget* widget = viewer->getWidget();
|
||||
while (widget && !widget->inherits("Gui::View3DInventor"))
|
||||
widget = widget->parentWidget();
|
||||
if (widget) {
|
||||
// this is the widget where the viewer is embedded
|
||||
Base::Type style = Base::Type::fromName((const char*)type);
|
||||
if (style != this->getTypeId()) {
|
||||
QEvent* event = new NavigationStyleEvent(style);
|
||||
QApplication::postEvent(widget, event);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (pickAction && used == pickAction) {
|
||||
// Execute the Pick Geometry command at this position
|
||||
auto cmd = Application::Instance->commandManager().getCommandByName("Std_PickGeometry");
|
||||
if (cmd && cmd->isActive()) {
|
||||
cmd->invoke(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* NavigationStyle::getPyObject()
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "BitmapFactory.h"
|
||||
#include "Command.h"
|
||||
#include "Document.h"
|
||||
#include "ViewProvider.h"
|
||||
|
||||
|
||||
FC_LOG_LEVEL_INIT("Selection", true, true, true)
|
||||
@@ -704,4 +705,127 @@ void SelectionView::onEnablePickList()
|
||||
|
||||
/// @endcond
|
||||
|
||||
// SelectionMenu implementation
|
||||
SelectionMenu::SelectionMenu(QWidget *parent)
|
||||
: QMenu(parent)
|
||||
{
|
||||
}
|
||||
|
||||
struct ElementInfo {
|
||||
QMenu *menu = nullptr;
|
||||
QIcon icon;
|
||||
std::vector<int> indices;
|
||||
};
|
||||
|
||||
struct SubMenuInfo {
|
||||
QMenu *menu = nullptr;
|
||||
// Map from sub-object label to map from object path to element info
|
||||
std::map<std::string, std::map<std::string, ElementInfo> > items;
|
||||
};
|
||||
|
||||
PickData SelectionMenu::doPick(const std::vector<PickData> &sels)
|
||||
{
|
||||
clear();
|
||||
|
||||
std::map<std::string, std::vector<int>> typeGroups;
|
||||
|
||||
// Group selections by element type
|
||||
for (int i = 0; i < (int)sels.size(); ++i) {
|
||||
const auto &sel = sels[i];
|
||||
std::string elementType = "Other";
|
||||
|
||||
// Extract element type from element name
|
||||
if (!sel.element.empty()) {
|
||||
if (sel.element.find("Face") == 0) elementType = "Face";
|
||||
else if (sel.element.find("Edge") == 0) elementType = "Edge";
|
||||
else if (sel.element.find("Vertex") == 0) elementType = "Vertex";
|
||||
else if (sel.element.find("Wire") == 0) elementType = "Wire";
|
||||
else if (sel.element.find("Shell") == 0) elementType = "Shell";
|
||||
else if (sel.element.find("Solid") == 0) elementType = "Solid";
|
||||
}
|
||||
|
||||
typeGroups[elementType].push_back(i);
|
||||
}
|
||||
|
||||
// Create menu structure
|
||||
for (const auto &typeGroup : typeGroups) {
|
||||
const std::string &typeName = typeGroup.first;
|
||||
const std::vector<int> &indices = typeGroup.second;
|
||||
|
||||
if (indices.empty()) continue;
|
||||
|
||||
QMenu *typeMenu = nullptr;
|
||||
if (typeGroups.size() > 1) {
|
||||
typeMenu = addMenu(QString::fromUtf8(typeName.c_str()));
|
||||
}
|
||||
|
||||
for (int idx : indices) {
|
||||
const auto &sel = sels[idx];
|
||||
|
||||
QString text = QString::fromUtf8(sel.obj->Label.getValue());
|
||||
if (!sel.element.empty()) {
|
||||
text += QString::fromLatin1(" (") + QString::fromUtf8(sel.element.c_str()) + QString::fromLatin1(")");
|
||||
}
|
||||
|
||||
// Get icon from view provider
|
||||
QIcon icon;
|
||||
auto vp = Application::Instance->getViewProvider(sel.obj);
|
||||
if (vp) {
|
||||
icon = vp->getIcon();
|
||||
}
|
||||
|
||||
QAction *action;
|
||||
if (typeMenu) {
|
||||
action = typeMenu->addAction(icon, text);
|
||||
} else {
|
||||
action = addAction(icon, text);
|
||||
}
|
||||
action->setData(idx);
|
||||
}
|
||||
}
|
||||
|
||||
QAction* picked = this->exec(QCursor::pos());
|
||||
return onPicked(picked, sels);
|
||||
}
|
||||
|
||||
PickData SelectionMenu::onPicked(QAction *picked, const std::vector<PickData> &sels)
|
||||
{
|
||||
Gui::Selection().rmvPreselect();
|
||||
if (!picked)
|
||||
return PickData{};
|
||||
|
||||
int index = picked->data().toInt();
|
||||
if (index >= 0 && index < (int)sels.size()) {
|
||||
const auto &sel = sels[index];
|
||||
if (sel.obj) {
|
||||
Gui::Selection().addSelection(sel.docName.c_str(),
|
||||
sel.objName.c_str(),
|
||||
sel.subName.c_str());
|
||||
}
|
||||
return sel;
|
||||
}
|
||||
return PickData{};
|
||||
}
|
||||
|
||||
void SelectionMenu::onHover(QAction *action)
|
||||
{
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
// For now, just clear preselection on hover
|
||||
// Could be enhanced to preselect the hovered item
|
||||
Gui::Selection().rmvPreselect();
|
||||
}
|
||||
|
||||
bool SelectionMenu::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
return QMenu::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
void SelectionMenu::leaveEvent(QEvent *e)
|
||||
{
|
||||
Gui::Selection().rmvPreselect();
|
||||
QMenu::leaveEvent(e);
|
||||
}
|
||||
|
||||
#include "moc_SelectionView.cpp"
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "DockWindow.h"
|
||||
#include "Selection.h"
|
||||
#include <QMenu>
|
||||
#include <QPointer>
|
||||
|
||||
|
||||
class QListWidget;
|
||||
@@ -112,6 +114,44 @@ private:
|
||||
};
|
||||
|
||||
} // namespace DockWnd
|
||||
|
||||
// Simple selection data structure
|
||||
struct PickData {
|
||||
App::DocumentObject* obj;
|
||||
std::string element;
|
||||
std::string docName;
|
||||
std::string objName;
|
||||
std::string subName;
|
||||
};
|
||||
|
||||
// Add SelectionMenu class outside the DockWnd namespace
|
||||
class GuiExport SelectionMenu : public QMenu {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectionMenu(QWidget *parent=nullptr);
|
||||
|
||||
/** Populate and show the menu for picking geometry elements.
|
||||
*
|
||||
* @param sels: a list of geometry element references
|
||||
* @return Return the picked geometry reference
|
||||
*
|
||||
* The menu will be divided into submenus that are grouped by element type.
|
||||
*/
|
||||
PickData doPick(const std::vector<PickData> &sels);
|
||||
|
||||
public Q_SLOTS:
|
||||
void onHover(QAction *);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *, QEvent *) override;
|
||||
void leaveEvent(QEvent *e) override;
|
||||
PickData onPicked(QAction *, const std::vector<PickData> &sels);
|
||||
|
||||
private:
|
||||
QPointer<QMenu> activeMenu;
|
||||
QPointer<QAction> activeAction;
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
#endif // GUI_DOCKWND_SELECTIONVIEW_H
|
||||
|
||||
@@ -737,7 +737,8 @@ MenuItem* StdWorkbench::setupMenuBar() const
|
||||
<< "Separator"
|
||||
<< "Std_ProjectUtil"
|
||||
<< "Std_DlgParameter"
|
||||
<< "Std_DlgCustomize";
|
||||
<< "Std_DlgCustomize"
|
||||
<< "Std_PickGeometry";
|
||||
|
||||
// Macro
|
||||
auto macro = new MenuItem( menuBar );
|
||||
|
||||
Reference in New Issue
Block a user