Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
2edfb0b
Updates fork from upstream
tatatupi Jun 20, 2025
988ce45
wip: adds NodeValidationState info to NodeDelegateModel
tatatupi Jun 22, 2025
1c84750
makes the nodeObject red in case of invalid state and adds a tooltip …
tatatupi Jun 22, 2025
3007c23
adds warning state and adapts calculator example
tatatupi Jun 22, 2025
129d414
adds validation icon and adapts calculation example
tatatupi Jun 22, 2025
e0b0c4a
core improvements to develop node processing status
g-abilio Jul 14, 2025
e002f5b
first commit on the creation of a processing status example
g-abilio Jul 15, 2025
985b638
fixes nodeprocessingstatus cast
tatatupi Jul 15, 2025
3f55e35
creation of random gen example, and fix of icon color
g-abilio Jul 16, 2025
50c6bec
Connect delegate UI update signal
tatatupi Jul 17, 2025
1c09ecf
fix random number node dynamic
g-abilio Jul 17, 2025
50ce760
Merge pull request #5 from fabns-nano/codex/add-signal-for-ui-updates…
g-abilio Jul 17, 2025
f2c120f
clean up test code in multiplication node
g-abilio Jul 17, 2025
965eb15
Add per-node background color API
hudsonmiranda291 Jul 22, 2025
545c018
Updates fork from upstream
Gabrielnmds Aug 5, 2025
c34e6e3
adds version to Catch2 due to compatibility issues
tatatupi Aug 6, 2025
97d48d5
Merge branch 'paceholder:master' into master
tatatupi Sep 2, 2025
89ac7f2
Merge branch 'master' of https://github.com/paceholder/nodeeditor
tatatupi Sep 5, 2025
37c3bf8
solves conflict
tatatupi Sep 10, 2025
b23ecc3
revert unnecessary changes in multiplication model
tatatupi Sep 10, 2025
0a35f76
revert unnecessary changes
tatatupi Sep 10, 2025
a9be092
solve icon size and refactor NodeProcessingStatus code
g-abilio Sep 11, 2025
5530b72
update and merge new code
g-abilio Sep 11, 2025
03e311a
remove duplicate code
g-abilio Sep 11, 2025
a87ceb2
add space to better organize processing status in node display
g-abilio Sep 15, 2025
ae3715b
remove processing value default value
g-abilio Sep 19, 2025
eedc666
fix bugs in node processing status
g-abilio Sep 19, 2025
1298605
add Q_DECLARE_METATYPE to solve linux build problems
g-abilio Sep 24, 2025
47d948f
declaring metatype in the correct place
g-abilio Sep 24, 2025
21b55ac
Merge pull request #6 from fabns-nano/node_processing_status
g-abilio Oct 20, 2025
4f1b23c
fix undocommands module inclusion
g-abilio Dec 3, 2025
87e219b
solve conflicts
g-abilio Dec 3, 2025
dea439b
Merge pull request #7 from fabns-nano/codex/expose-setbackgroundcolor…
g-abilio Dec 3, 2025
cc81264
Adds node nickname functionality (#11)
g-abilio Dec 11, 2025
83426c7
Merge branch 'master' of https://github.com/paceholder/nodeeditor int…
Gabrielnmds Dec 17, 2025
422cdd5
adds zoomFitAll and zoomFitSelected methods
Gabrielnmds Aug 5, 2025
9fdc3e8
refactor(zoom fit): minor code fixes
Gabrielnmds Dec 15, 2025
f2473e7
refactor(zoom fit): fixes code conflicts
Gabrielnmds Dec 17, 2025
bc1b6cb
Refactor(node editor): solve conflicts
Gabrielnmds Dec 17, 2025
2b3ac01
fix uninitialized variable and remove return
g-abilio Jan 7, 2026
891b702
remove bad coupling
g-abilio Jan 7, 2026
8d6c26b
refactoring to simpler code
g-abilio Jan 12, 2026
e828567
solve conflicts
g-abilio Jan 14, 2026
02b3969
Revert "solve conflicts"
g-abilio Jan 15, 2026
5d1d07b
remove example implementation of zoom fit feature
g-abilio Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions include/QtNodes/internal/AbstractGraphModel.hpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
#pragma once

#include "Export.hpp"
#include "ConnectionIdHash.hpp"
#include "Definitions.hpp"
#include "Export.hpp"

#include <QtCore/QJsonObject>
#include <QtCore/QObject>
#include <QtCore/QVariant>

#include <unordered_set>


namespace QtNodes {

/**
Expand Down Expand Up @@ -51,8 +50,7 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
*/
virtual std::unordered_set<ConnectionId> connections(NodeId nodeId,
PortType portType,
PortIndex index) const
= 0;
PortIndex index) const = 0;

/// Checks if two nodes with the given `connectionId` are connected.
virtual bool connectionExists(ConnectionId const connectionId) const = 0;
Expand Down Expand Up @@ -132,8 +130,10 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
* @returns Port Data Type, Port Data, Connection Policy, Port
* Caption.
*/
virtual QVariant portData(NodeId nodeId, PortType portType, PortIndex index, PortRole role) const
= 0;
virtual QVariant portData(NodeId nodeId,
PortType portType,
PortIndex index,
PortRole role) const = 0;

/**
* A utility function that unwraps the `QVariant` value returned from the
Expand Down Expand Up @@ -188,6 +188,8 @@ class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject

virtual bool loopsEnabled() const { return true; }

virtual bool nodeZoomFitMenu(NodeId) { return false; }

public:
/**
* Function clears connections attached to the ports that are scheduled to be
Expand Down
6 changes: 5 additions & 1 deletion include/QtNodes/internal/BasicGraphicsScene.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include <tuple>
#include <unordered_map>


class QUndoStack;

namespace QtNodes {
Expand Down Expand Up @@ -112,6 +111,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);
Expand All @@ -125,6 +126,9 @@ 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:
/**
Expand Down
8 changes: 5 additions & 3 deletions include/QtNodes/internal/DataFlowGraphModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

namespace QtNodes {

class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public Serializable
class NODE_EDITOR_PUBLIC DataFlowGraphModel
: public AbstractGraphModel
, public Serializable
{
Q_OBJECT

Expand Down Expand Up @@ -43,7 +45,6 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public

NodeId addNode(QString const nodeType) override;


bool connectionPossible(ConnectionId const connectionId) const override;

void addConnection(ConnectionId const connectionId) override;
Expand Down Expand Up @@ -75,7 +76,6 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public

void loadNode(QJsonObject const &nodeJson) override;


// From Serializable
QJsonObject save() const override;

Expand All @@ -101,6 +101,8 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public
/// Loops do not make any sense in uni-direction data propagation
bool loopsEnabled() const override { return false; }

bool nodeZoomFitMenu(NodeId) override;

Q_SIGNALS:
void inPortDataWasSet(NodeId const, PortType const, PortIndex const);

Expand Down
4 changes: 4 additions & 0 deletions include/QtNodes/internal/GraphicsView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ public Q_SLOTS:

virtual void onPasteObjects();

void zoomFitAll();

void zoomFitSelected();

Q_SIGNALS:
void scaleChanged(double scale);

Expand Down
6 changes: 6 additions & 0 deletions include/QtNodes/internal/NodeDelegateModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,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 &) {}
Expand Down Expand Up @@ -188,6 +192,8 @@ public Q_SLOTS:
NodeValidationState _nodeValidationState;

NodeProcessingStatus _processingStatus{NodeProcessingStatus::NoStatus};

bool _zoomFitMenu{false};
};

} // namespace QtNodes
Expand Down
68 changes: 68 additions & 0 deletions src/BasicGraphicsScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

#include <QUndoStack>

#include <QHeaderView>
#include <QLineEdit>
#include <QTreeWidget>
#include <QWidgetAction>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QGraphicsSceneMoveEvent>

Expand Down Expand Up @@ -203,6 +207,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();
Expand Down
11 changes: 11 additions & 0 deletions src/DataFlowGraphModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -646,4 +646,15 @@ void DataFlowGraphModel::propagateEmptyDataTo(NodeId const nodeId, PortIndex con
setPortData(nodeId, PortType::In, portIndex, emptyData, PortRole::Data);
}

bool DataFlowGraphModel::nodeZoomFitMenu(NodeId const nodeId)
{
bool isZoomFitMenu = false;
auto delegate = delegateModel<NodeDelegateModel>(nodeId);

if (delegate)
isZoomFitMenu = delegate->zoomFitMenu();

return isZoomFitMenu;
}

} // namespace QtNodes
68 changes: 54 additions & 14 deletions src/GraphicsView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "BasicGraphicsScene.hpp"
#include "ConnectionGraphicsObject.hpp"
#include "DataFlowGraphModel.hpp"
#include "NodeGraphicsObject.hpp"
#include "StyleCollection.hpp"
#include "UndoCommands.hpp"
Expand All @@ -23,7 +24,9 @@
#include <cmath>

using QtNodes::BasicGraphicsScene;
using QtNodes::DataFlowGraphModel;
using QtNodes::GraphicsView;
using QtNodes::NodeGraphicsObject;

GraphicsView::GraphicsView(QWidget *parent)
: QGraphicsView(parent)
Expand Down Expand Up @@ -75,8 +78,7 @@ QAction *GraphicsView::deleteSelectionAction() const
void GraphicsView::setScene(BasicGraphicsScene *scene)
{
QGraphicsView::setScene(scene);
if (!scene)
{
if (!scene) {
// Clear actions.
delete _clearSelectionAction;
delete _deleteSelectionAction;
Expand Down Expand Up @@ -162,6 +164,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()
Expand All @@ -181,16 +190,23 @@ void GraphicsView::centerScene()

void GraphicsView::contextMenuEvent(QContextMenuEvent *event)
{
if (itemAt(event->pos())) {
QGraphicsView::contextMenuEvent(event);
return;
}
QGraphicsView::contextMenuEvent(event);
QMenu *menu = nullptr;

if (!nodeScene()) return;
bool isZoomFitMenu = false;

auto const scenePos = mapToScene(event->pos());
auto *dfModel = &nodeScene()->graphModel();
auto n = qgraphicsitem_cast<NodeGraphicsObject *>(itemAt(event->pos()));

QMenu *menu = nodeScene()->createSceneMenu(scenePos);
if (dfModel && n) {
isZoomFitMenu = dfModel->nodeZoomFitMenu(n->nodeId());
}

if (itemAt(event->pos()) && isZoomFitMenu) {
menu = nodeScene()->createZoomMenu(mapToScene(event->pos()));
} else if (!itemAt(event->pos())) {
menu = nodeScene()->createSceneMenu(mapToScene(event->pos()));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple thoughts here:

  1. There's already a signal we could connect to for node-specific context menus (i.e. on node right clicked) so this might not actually be the best place for checking for itemAt() and calling our menu code.
    https://github.com/search?q=repo%3Apaceholder%2Fnodeeditor%20nodeContextMenu&type=code
  2. Zoom to fit selected (or all) aren't really specific to the node you're right clicking. Shouldn't these menu items be added to the more general scene menu? That way, right clicking empty space brings up the zoom options, but right clicking a node brings up the node-specific actions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll check that signal and try changing how the verification and call are made. Thanks for the suggestion!

Regarding the zoom fit options, the feature works as follows:

  1. Zoom Fit Selected zoom fits the selected nodes.
  2. Zoom Fit All zoom fits all the nodes in the canvas.

The right-click context menu is only triggered when the right button is on a selected node because right-clicking on the empty screen triggers the node context menu. Therefore, we thought it would be better to do this so as not to create an extensive context menu with multiple options (i.e., nodes and node-specific actions).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember that this is just one example of how to use the Zoom Fit feature. Therefore, this feature can be implemented in the application in many ways by the developer.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tagging @paceholder for final review on this bit.
If he's cool with it, I'm not going to dig my heels in.


if (menu) {
menu->exec(event->globalPos());
Expand Down Expand Up @@ -291,14 +307,16 @@ void GraphicsView::setupScale(double scale)

void GraphicsView::onDeleteSelectedObjects()
{
if (!nodeScene()) return;
if (!nodeScene())
return;

nodeScene()->undoStack().push(new DeleteCommand(nodeScene()));
}

void GraphicsView::onDuplicateSelectedObjects()
{
if (!nodeScene()) return;
if (!nodeScene())
return;

QPointF const pastePosition = scenePastePosition();

Expand All @@ -308,14 +326,16 @@ void GraphicsView::onDuplicateSelectedObjects()

void GraphicsView::onCopySelectedObjects()
{
if (!nodeScene()) return;
if (!nodeScene())
return;

nodeScene()->undoStack().push(new CopyCommand(nodeScene()));
}

void GraphicsView::onPasteObjects()
{
if (!nodeScene()) return;
if (!nodeScene())
return;

QPointF const pastePosition = scenePastePosition();
nodeScene()->undoStack().push(new PasteCommand(nodeScene(), pastePosition));
Expand Down Expand Up @@ -360,7 +380,8 @@ void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
QGraphicsView::mouseMoveEvent(event);

if (!scene()) return;
if (!scene())
return;

if (scene()->mouseGrabberItem() == nullptr && event->buttons() == Qt::LeftButton) {
// Make sure shift is not being pressed
Expand Down Expand Up @@ -434,3 +455,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);
}
}