#include "FlowView.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "FlowScene.hpp"
#include "DataModelRegistry.hpp"
#include "Node.hpp"
#include "NodeGraphicsObject.hpp"
#include "ConnectionGraphicsObject.hpp"
#include "StyleCollection.hpp"
using QtNodes::FlowView;
using QtNodes::FlowScene;
FlowView::
FlowView(QWidget *parent)
: QGraphicsView(parent)
, _clearSelectionAction(Q_NULLPTR)
, _deleteSelectionAction(Q_NULLPTR)
, _scene(Q_NULLPTR)
{
setDragMode(QGraphicsView::ScrollHandDrag);
setRenderHint(QPainter::Antialiasing);
auto const &flowViewStyle = StyleCollection::flowViewStyle();
setBackgroundBrush(flowViewStyle.BackgroundColor);
//setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
//setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
setCacheMode(QGraphicsView::CacheBackground);
//setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
}
FlowView::
FlowView(FlowScene *scene, QWidget *parent)
: FlowView(parent)
{
setScene(scene);
}
QAction*
FlowView::
clearSelectionAction() const
{
return _clearSelectionAction;
}
QAction*
FlowView::
deleteSelectionAction() const
{
return _deleteSelectionAction;
}
void
FlowView::setScene(FlowScene *scene)
{
_scene = scene;
QGraphicsView::setScene(_scene);
// setup actions
delete _clearSelectionAction;
_clearSelectionAction = new QAction(QStringLiteral("Clear Selection"), this);
_clearSelectionAction->setShortcut(Qt::Key_Escape);
connect(_clearSelectionAction, &QAction::triggered, _scene, &QGraphicsScene::clearSelection);
addAction(_clearSelectionAction);
delete _deleteSelectionAction;
_deleteSelectionAction = new QAction(QStringLiteral("Delete Selection"), this);
_deleteSelectionAction->setShortcut(Qt::Key_Delete);
connect(_deleteSelectionAction, &QAction::triggered, this, &FlowView::deleteSelectedNodes);
addAction(_deleteSelectionAction);
}
void
FlowView::
contextMenuEvent(QContextMenuEvent *event)
{
if (itemAt(event->pos()))
{
QGraphicsView::contextMenuEvent(event);
return;
}
QMenu modelMenu;
auto skipText = QStringLiteral("skip me");
//Add filterbox to the context menu
auto *txtBox = new QLineEdit(&modelMenu);
txtBox->setPlaceholderText(QStringLiteral("Filter"));
txtBox->setClearButtonEnabled(true);
auto *txtBoxAction = new QWidgetAction(&modelMenu);
txtBoxAction->setDefaultWidget(txtBox);
modelMenu.addAction(txtBoxAction);
//Add result treeview to the context menu
auto *treeView = new QTreeWidget(&modelMenu);
treeView->header()->close();
auto *treeViewAction = new QWidgetAction(&modelMenu);
treeViewAction->setDefaultWidget(treeView);
modelMenu.addAction(treeViewAction);
QMap topLevelItems;
for (auto const &cat : _scene->registry().categories())
{
auto item = new QTreeWidgetItem(treeView);
item->setText(0, cat);
item->setData(0, Qt::UserRole, skipText);
topLevelItems[cat] = item;
}
for (auto const &assoc : _scene->registry().registeredModelsCategoryAssociation())
{
auto parent = topLevelItems[assoc.second];
auto item = new QTreeWidgetItem(parent);
item->setText(0, assoc.first);
item->setData(0, Qt::UserRole, assoc.first);
}
treeView->expandAll();
connect(treeView, &QTreeWidget::itemClicked, [&](QTreeWidgetItem *item, int)
{
QString modelName = item->data(0, Qt::UserRole).toString();
if (modelName == skipText)
{
return;
}
auto type = _scene->registry().create(modelName);
if (type)
{
auto& node = _scene->createNode(std::move(type));
QPoint pos = event->pos();
QPointF posView = this->mapToScene(pos);
node.nodeGraphicsObject().setPos(posView);
_scene->nodePlaced(node);
}
else
{
qDebug() childCount(); ++i)
{
auto child = topLvlItem->child(i);
auto modelName = child->data(0, Qt::UserRole).toString();
const bool match = (modelName.contains(text, Qt::CaseInsensitive));
child->setHidden(!match);
}
}
});
// make sure the text box gets focus so the user doesn't have to click on it
txtBox->setFocus();
modelMenu.exec(event->globalPos());
}
void
FlowView::
wheelEvent(QWheelEvent *event)
{
QPoint delta = event->angleDelta();
if (delta.y() == 0)
{
event->ignore();
return;
}
double const d = delta.y() / std::abs(delta.y());
if (d > 0.0)
scaleUp();
else
scaleDown();
}
void
FlowView::
scaleUp()
{
double const step = 1.2;
double const factor = std::pow(step, 1.0);
QTransform t = transform();
if (t.m11() > 2.0)
return;
scale(factor, factor);
}
void
FlowView::
scaleDown()
{
double const step = 1.2;
double const factor = std::pow(step, -1.0);
scale(factor, factor);
}
void
FlowView::
deleteSelectedNodes()
{
// Delete the selected connections first, ensuring that they won't be
// automatically deleted when selected nodes are deleted (deleting a node
// deletes some connections as well)
for (QGraphicsItem * item : _scene->selectedItems())
{
if (auto c = qgraphicsitem_cast(item))
_scene->deleteConnection(c->connection());
}
// Delete the nodes; this will delete many of the connections.
// Selected connections were already deleted prior to this loop, otherwise
// qgraphicsitem_cast(item) could be a use-after-free
// when a selected connection is deleted by deleting the node.
for (QGraphicsItem * item : _scene->selectedItems())
{
if (auto n = qgraphicsitem_cast(item))
_scene->removeNode(n->node());
}
}
void
FlowView::
keyPressEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_Shift:
setDragMode(QGraphicsView::RubberBandDrag);
break;
default:
break;
}
QGraphicsView::keyPressEvent(event);
}
void
FlowView::
keyReleaseEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_Shift:
setDragMode(QGraphicsView::ScrollHandDrag);
break;
default:
break;
}
QGraphicsView::keyReleaseEvent(event);
}
void
FlowView::
mousePressEvent(QMouseEvent *event)
{
QGraphicsView::mousePressEvent(event);
if (event->button() == Qt::LeftButton)
{
_clickPos = mapToScene(event->pos());
}
}
void
FlowView::
mouseMoveEvent(QMouseEvent *event)
{
QGraphicsView::mouseMoveEvent(event);
if (scene()->mouseGrabberItem() == nullptr && event->buttons() == Qt::LeftButton)
{
// Make sure shift is not being pressed
if ((event->modifiers() & Qt::ShiftModifier) == 0)
{
QPointF difference = _clickPos - mapToScene(event->pos());
setSceneRect(sceneRect().translated(difference.x(), difference.y()));
}
}
}
void
FlowView::
drawBackground(QPainter* painter, const QRectF& r)
{
QGraphicsView::drawBackground(painter, r);
auto drawGrid =
[&](double gridStep)
{
QRect windowRect = rect();
QPointF tl = mapToScene(windowRect.topLeft());
QPointF br = mapToScene(windowRect.bottomRight());
double left = std::floor(tl.x() / gridStep - 0.5);
double right = std::floor(br.x() / gridStep + 1.0);
double bottom = std::floor(tl.y() / gridStep - 0.5);
double top = std::floor (br.y() / gridStep + 1.0);
// vertical lines
for (int xi = int(left); xi drawLine(line);
}
// horizontal lines
for (int yi = int(bottom); yi drawLine(line);
}
};
auto const &flowViewStyle = StyleCollection::flowViewStyle();
QBrush bBrush = backgroundBrush();
QPen pfine(flowViewStyle.FineGridColor, 1.0);
painter->setPen(pfine);
drawGrid(15);
QPen p(flowViewStyle.CoarseGridColor, 1.0);
painter->setPen(p);
drawGrid(150);
}
void
FlowView::
showEvent(QShowEvent *event)
{
_scene->setSceneRect(this->rect());
QGraphicsView::showEvent(event);
}
FlowScene *
FlowView::
scene()
{
return _scene;
}