diff --git a/include/QtNodes/internal/BasicGraphicsScene.hpp b/include/QtNodes/internal/BasicGraphicsScene.hpp index 83424c5d8..1d66318dd 100644 --- a/include/QtNodes/internal/BasicGraphicsScene.hpp +++ b/include/QtNodes/internal/BasicGraphicsScene.hpp @@ -110,6 +110,8 @@ class NODE_EDITOR_PUBLIC BasicGraphicsScene : public QGraphicsScene */ virtual QMenu *createSceneMenu(QPointF const scenePos); + QMenu *createZoomMenu(QPointF const scenePos); + Q_SIGNALS: void modified(BasicGraphicsScene *); void nodeMoved(NodeId const nodeId, QPointF const &newLocation); @@ -124,6 +126,10 @@ class NODE_EDITOR_PUBLIC BasicGraphicsScene : public QGraphicsScene /// Signal allows showing custom context menu upon clicking a node. void nodeContextMenu(NodeId const nodeId, QPointF const pos); + /// Signals to call Graphics View's zoomFit methods + void zoomFitAllClicked(); + void zoomFitSelectedClicked(); + private: /** * @brief Creates Node and Connection graphics objects. diff --git a/include/QtNodes/internal/GraphicsView.hpp b/include/QtNodes/internal/GraphicsView.hpp index 1fbe5a890..89a7d774c 100644 --- a/include/QtNodes/internal/GraphicsView.hpp +++ b/include/QtNodes/internal/GraphicsView.hpp @@ -61,6 +61,10 @@ public Q_SLOTS: void onPasteObjects(); + void zoomFitAll(); + + void zoomFitSelected(); + Q_SIGNALS: void scaleChanged(double scale); diff --git a/include/QtNodes/internal/NodeDelegateModel.hpp b/include/QtNodes/internal/NodeDelegateModel.hpp index 5215c3692..c81a9e311 100644 --- a/include/QtNodes/internal/NodeDelegateModel.hpp +++ b/include/QtNodes/internal/NodeDelegateModel.hpp @@ -141,6 +141,10 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel virtual bool resizable() const { return false; } + bool zoomFitMenu() const { return _zoomFitMenu; } + + void setZoomFitMenu(bool state) { _zoomFitMenu = state; } + public Q_SLOTS: virtual void inputConnectionCreated(ConnectionId const &) {} virtual void inputConnectionDeleted(ConnectionId const &) {} @@ -196,6 +200,7 @@ public Q_SLOTS: NodeValidationState _nodeValidationState; + bool _zoomFitMenu{false}; NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus}; }; diff --git a/src/BasicGraphicsScene.cpp b/src/BasicGraphicsScene.cpp index 7cec5ec45..c612994ca 100644 --- a/src/BasicGraphicsScene.cpp +++ b/src/BasicGraphicsScene.cpp @@ -11,6 +11,10 @@ #include +#include +#include +#include +#include #include #include @@ -198,6 +202,70 @@ QMenu *BasicGraphicsScene::createSceneMenu(QPointF const scenePos) return nullptr; } +QMenu *BasicGraphicsScene::createZoomMenu(QPointF const scenePos) +{ + Q_UNUSED(scenePos); + + QMenu *menu = new QMenu(); + + auto *txtBox = new QLineEdit(menu); + txtBox->setPlaceholderText(QStringLiteral("Filter")); + txtBox->setClearButtonEnabled(true); + + auto *txtBoxAction = new QWidgetAction(menu); + txtBoxAction->setDefaultWidget(txtBox); + menu->addAction(txtBoxAction); + + QTreeWidget *treeView = new QTreeWidget(menu); + treeView->header()->close(); + + treeView->setMaximumHeight(100); + treeView->setMaximumWidth(150); + + auto *treeViewAction = new QWidgetAction(menu); + treeViewAction->setDefaultWidget(treeView); + menu->addAction(treeViewAction); + + auto freezeItem = new QTreeWidgetItem(treeView); + freezeItem->setText(0, "Zoom Fit All"); + + auto unfreezeItem = new QTreeWidgetItem(treeView); + unfreezeItem->setText(0, "Zoom Fit Selected"); + + treeView->expandAll(); + + connect(treeView, &QTreeWidget::itemClicked, [this, menu](QTreeWidgetItem *item, int) { + if (item->text(0) == "Zoom Fit All") { + Q_EMIT zoomFitAllClicked(); + + menu->close(); + return; + } + if (item->text(0) == "Zoom Fit Selected") { + Q_EMIT zoomFitSelectedClicked(); + + menu->close(); + return; + } + }); + + // Filter + connect(txtBox, &QLineEdit::textChanged, [treeView](const QString &text) { + QTreeWidgetItemIterator it(treeView); + while (*it) { + auto modelName = (*it)->text(0); + const bool match = (modelName.contains(text, Qt::CaseInsensitive)); + (*it)->setHidden(!match); + ++it; + } + }); + + txtBox->setFocus(); + menu->setAttribute(Qt::WA_DeleteOnClose); + + return menu; +} + void BasicGraphicsScene::traverseGraphAndPopulateGraphicsObjects() { auto allNodeIds = _graphModel.allNodeIds(); diff --git a/src/GraphicsView.cpp b/src/GraphicsView.cpp index cd19581eb..81a53de2c 100644 --- a/src/GraphicsView.cpp +++ b/src/GraphicsView.cpp @@ -2,6 +2,7 @@ #include "BasicGraphicsScene.hpp" #include "ConnectionGraphicsObject.hpp" +#include "DataFlowGraphModel.hpp" #include "Definitions.hpp" #include "NodeGraphicsObject.hpp" #include "StyleCollection.hpp" @@ -26,6 +27,7 @@ #include using QtNodes::BasicGraphicsScene; +using QtNodes::DataFlowGraphModel; using QtNodes::GraphicsView; GraphicsView::GraphicsView(QWidget *parent) @@ -150,6 +152,13 @@ void GraphicsView::setScene(BasicGraphicsScene *scene) auto redoAction = scene->undoStack().createRedoAction(this, tr("&Redo")); redoAction->setShortcuts(QKeySequence::Redo); addAction(redoAction); + + /// Connections to context menu funcionality + connect(scene, &BasicGraphicsScene::zoomFitAllClicked, this, &GraphicsView::zoomFitAll); + connect(scene, + &BasicGraphicsScene::zoomFitSelectedClicked, + this, + &GraphicsView::zoomFitSelected); } void GraphicsView::centerScene() @@ -169,18 +178,30 @@ void GraphicsView::centerScene() void GraphicsView::contextMenuEvent(QContextMenuEvent *event) { - if (itemAt(event->pos())) { - QGraphicsView::contextMenuEvent(event); - return; - } + QGraphicsView::contextMenuEvent(event); + QMenu *menu = nullptr; - auto const scenePos = mapToScene(event->pos()); + bool isZoomFitMenu; + + if (auto *dfModel = dynamic_cast(&nodeScene()->graphModel())) { + if (auto n = qgraphicsitem_cast(itemAt(event->pos()))) { + if (auto *delegate = dfModel->delegateModel(n->nodeId())) { + isZoomFitMenu = delegate->zoomFitMenu(); + } + } + } + if (itemAt(event->pos()) && isZoomFitMenu) { + menu = nodeScene()->createZoomMenu(mapToScene(event->pos())); - QMenu *menu = nodeScene()->createSceneMenu(scenePos); + } else if (!itemAt(event->pos())) { + menu = nodeScene()->createSceneMenu(mapToScene(event->pos())); + } if (menu) { menu->exec(event->globalPos()); } + + return; } void GraphicsView::wheelEvent(QWheelEvent *event) @@ -477,3 +498,22 @@ QPointF GraphicsView::scenePastePosition() return mapToScene(origin); } + +void GraphicsView::zoomFitAll() +{ + fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio); +} + +void GraphicsView::zoomFitSelected() +{ + if (scene()->selectedItems().count() > 0) { + QRectF unitedBoundingRect{}; + + for (QGraphicsItem *item : scene()->selectedItems()) { + unitedBoundingRect = unitedBoundingRect.united( + item->mapRectToScene(item->boundingRect())); + } + + fitInView(unitedBoundingRect, Qt::KeepAspectRatio); + } +}