open-story-teller/story-editor-legacy/nodeeditor/src/BasicGraphicsScene.cpp
2023-12-13 13:44:23 +01:00

309 lines
8.1 KiB
C++

#include "BasicGraphicsScene.hpp"
#include "AbstractNodeGeometry.hpp"
#include "ConnectionGraphicsObject.hpp"
#include "ConnectionIdUtils.hpp"
#include "DefaultHorizontalNodeGeometry.hpp"
#include "DefaultNodePainter.hpp"
#include "DefaultVerticalNodeGeometry.hpp"
#include "GraphicsView.hpp"
#include "NodeGraphicsObject.hpp"
#include <QUndoStack>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QGraphicsSceneMoveEvent>
#include <QtCore/QBuffer>
#include <QtCore/QByteArray>
#include <QtCore/QDataStream>
#include <QtCore/QFile>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QtGlobal>
#include <iostream>
#include <stdexcept>
#include <unordered_set>
#include <utility>
#include <queue>
namespace QtNodes {
BasicGraphicsScene::BasicGraphicsScene(AbstractGraphModel &graphModel, QObject *parent)
: QGraphicsScene(parent)
, _graphModel(graphModel)
, _nodeGeometry(std::make_unique<DefaultHorizontalNodeGeometry>(_graphModel))
, _nodePainter(std::make_unique<DefaultNodePainter>())
, _nodeDrag(false)
, _undoStack(new QUndoStack(this))
, _orientation(Qt::Horizontal)
{
setItemIndexMethod(QGraphicsScene::NoIndex);
connect(&_graphModel,
&AbstractGraphModel::connectionCreated,
this,
&BasicGraphicsScene::onConnectionCreated);
connect(&_graphModel,
&AbstractGraphModel::connectionDeleted,
this,
&BasicGraphicsScene::onConnectionDeleted);
connect(&_graphModel,
&AbstractGraphModel::nodeCreated,
this,
&BasicGraphicsScene::onNodeCreated);
connect(&_graphModel,
&AbstractGraphModel::nodeDeleted,
this,
&BasicGraphicsScene::onNodeDeleted);
connect(&_graphModel,
&AbstractGraphModel::nodePositionUpdated,
this,
&BasicGraphicsScene::onNodePositionUpdated);
connect(&_graphModel,
&AbstractGraphModel::nodeUpdated,
this,
&BasicGraphicsScene::onNodeUpdated);
connect(this, &BasicGraphicsScene::nodeClicked, this, &BasicGraphicsScene::onNodeClicked);
connect(&_graphModel, &AbstractGraphModel::modelReset, this, &BasicGraphicsScene::onModelReset);
traverseGraphAndPopulateGraphicsObjects();
}
BasicGraphicsScene::~BasicGraphicsScene() = default;
AbstractGraphModel const &BasicGraphicsScene::graphModel() const
{
return _graphModel;
}
AbstractGraphModel &BasicGraphicsScene::graphModel()
{
return _graphModel;
}
AbstractNodeGeometry &BasicGraphicsScene::nodeGeometry()
{
return *_nodeGeometry;
}
AbstractNodePainter &BasicGraphicsScene::nodePainter()
{
return *_nodePainter;
}
void BasicGraphicsScene::setNodePainter(std::unique_ptr<AbstractNodePainter> newPainter)
{
_nodePainter = std::move(newPainter);
}
QUndoStack &BasicGraphicsScene::undoStack()
{
return *_undoStack;
}
void BasicGraphicsScene::setDropShadowEffect(bool enable)
{
_dropShadowEffect = enable;
}
bool BasicGraphicsScene::isDropShadowEffectEnabled() const
{
return _dropShadowEffect;
}
std::unique_ptr<ConnectionGraphicsObject> const &BasicGraphicsScene::makeDraftConnection(
ConnectionId const incompleteConnectionId)
{
_draftConnection = std::make_unique<ConnectionGraphicsObject>(*this, incompleteConnectionId);
_draftConnection->grabMouse();
return _draftConnection;
}
void BasicGraphicsScene::resetDraftConnection()
{
_draftConnection.reset();
}
void BasicGraphicsScene::clearScene()
{
auto const &allNodeIds = graphModel().allNodeIds();
for (auto nodeId : allNodeIds) {
graphModel().deleteNode(nodeId);
}
}
NodeGraphicsObject *BasicGraphicsScene::nodeGraphicsObject(NodeId nodeId)
{
NodeGraphicsObject *ngo = nullptr;
auto it = _nodeGraphicsObjects.find(nodeId);
if (it != _nodeGraphicsObjects.end()) {
ngo = it->second.get();
}
return ngo;
}
ConnectionGraphicsObject *BasicGraphicsScene::connectionGraphicsObject(ConnectionId connectionId)
{
ConnectionGraphicsObject *cgo = nullptr;
auto it = _connectionGraphicsObjects.find(connectionId);
if (it != _connectionGraphicsObjects.end()) {
cgo = it->second.get();
}
return cgo;
}
void BasicGraphicsScene::setOrientation(Qt::Orientation const orientation)
{
if (_orientation != orientation) {
_orientation = orientation;
switch (_orientation) {
case Qt::Horizontal:
_nodeGeometry = std::make_unique<DefaultHorizontalNodeGeometry>(_graphModel);
break;
case Qt::Vertical:
_nodeGeometry = std::make_unique<DefaultVerticalNodeGeometry>(_graphModel);
break;
}
onModelReset();
}
}
QMenu *BasicGraphicsScene::createSceneMenu(QPointF const scenePos)
{
Q_UNUSED(scenePos);
return nullptr;
}
void BasicGraphicsScene::traverseGraphAndPopulateGraphicsObjects()
{
auto allNodeIds = _graphModel.allNodeIds();
// First create all the nodes.
for (NodeId const nodeId : allNodeIds) {
_nodeGraphicsObjects[nodeId] = std::make_unique<NodeGraphicsObject>(*this, nodeId);
}
// Then for each node check output connections and insert them.
for (NodeId const nodeId : allNodeIds) {
unsigned int nOutPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::OutPortCount);
for (PortIndex index = 0; index < nOutPorts; ++index) {
auto const &outConnectionIds = _graphModel.connections(nodeId, PortType::Out, index);
for (auto cid : outConnectionIds) {
_connectionGraphicsObjects[cid] = std::make_unique<ConnectionGraphicsObject>(*this,
cid);
}
}
}
}
void BasicGraphicsScene::updateAttachedNodes(ConnectionId const connectionId,
PortType const portType)
{
auto node = nodeGraphicsObject(getNodeId(portType, connectionId));
if (node) {
node->update();
}
}
void BasicGraphicsScene::onConnectionDeleted(ConnectionId const connectionId)
{
auto it = _connectionGraphicsObjects.find(connectionId);
if (it != _connectionGraphicsObjects.end()) {
_connectionGraphicsObjects.erase(it);
}
// TODO: do we need it?
if (_draftConnection && _draftConnection->connectionId() == connectionId) {
_draftConnection.reset();
}
updateAttachedNodes(connectionId, PortType::Out);
updateAttachedNodes(connectionId, PortType::In);
}
void BasicGraphicsScene::onConnectionCreated(ConnectionId const connectionId)
{
_connectionGraphicsObjects[connectionId]
= std::make_unique<ConnectionGraphicsObject>(*this, connectionId);
updateAttachedNodes(connectionId, PortType::Out);
updateAttachedNodes(connectionId, PortType::In);
}
void BasicGraphicsScene::onNodeDeleted(NodeId const nodeId)
{
auto it = _nodeGraphicsObjects.find(nodeId);
if (it != _nodeGraphicsObjects.end()) {
_nodeGraphicsObjects.erase(it);
}
}
void BasicGraphicsScene::onNodeCreated(NodeId const nodeId)
{
_nodeGraphicsObjects[nodeId] = std::make_unique<NodeGraphicsObject>(*this, nodeId);
}
void BasicGraphicsScene::onNodePositionUpdated(NodeId const nodeId)
{
auto node = nodeGraphicsObject(nodeId);
if (node) {
node->setPos(_graphModel.nodeData(nodeId, NodeRole::Position).value<QPointF>());
node->update();
_nodeDrag = true;
}
}
void BasicGraphicsScene::onNodeUpdated(NodeId const nodeId)
{
auto node = nodeGraphicsObject(nodeId);
if (node) {
node->setGeometryChanged();
_nodeGeometry->recomputeSize(nodeId);
node->update();
node->moveConnections();
}
}
void BasicGraphicsScene::onNodeClicked(NodeId const nodeId)
{
if (_nodeDrag)
Q_EMIT nodeMoved(nodeId, _graphModel.nodeData(nodeId, NodeRole::Position).value<QPointF>());
_nodeDrag = false;
}
void BasicGraphicsScene::onModelReset()
{
_connectionGraphicsObjects.clear();
_nodeGraphicsObjects.clear();
clear();
traverseGraphAndPopulateGraphicsObjects();
}
} // namespace QtNodes