nodeview can connect and disconnect nodes
This commit is contained in:
+3
-1
@@ -87,7 +87,7 @@ bool NodeParam::AreDataTypesCompatible(const DataType &output_type, const QList<
|
||||
return false;
|
||||
}
|
||||
|
||||
void NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
|
||||
NodeEdgePtr NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
|
||||
{
|
||||
// If the input can only accept one input (the default) and has one already, disconnect it
|
||||
if (!input->edges_.isEmpty() && !input->can_accept_multiple_inputs()) {
|
||||
@@ -98,6 +98,8 @@ void NodeParam::ConnectEdge(NodeOutput *output, NodeInput *input)
|
||||
|
||||
output->edges_.append(edge);
|
||||
input->edges_.append(edge);
|
||||
|
||||
return edge;
|
||||
}
|
||||
|
||||
void NodeParam::DisconnectEdge(NodeEdgePtr edge)
|
||||
|
||||
+2
-3
@@ -34,8 +34,7 @@ class NodeParam : public QObject
|
||||
public:
|
||||
enum Type {
|
||||
kInput,
|
||||
kOutput,
|
||||
kBidirectional
|
||||
kOutput
|
||||
};
|
||||
|
||||
enum DataType {
|
||||
@@ -69,7 +68,7 @@ public:
|
||||
static bool AreDataTypesCompatible(const DataType& output_type, const DataType& input_type);
|
||||
static bool AreDataTypesCompatible(const DataType& output_type, const QList<DataType>& input_types);
|
||||
|
||||
static void ConnectEdge(NodeOutput *output, NodeInput *input);
|
||||
static NodeEdgePtr ConnectEdge(NodeOutput *output, NodeInput *input);
|
||||
static void DisconnectEdge(NodeEdgePtr edge);
|
||||
|
||||
static QString GetDefaultDataTypeName(const DataType &type);
|
||||
|
||||
@@ -68,6 +68,8 @@ QTreeView, QListView, QLineEdit, QMenu, QProgressBar, QPushButton::checked, Node
|
||||
background: #191919;
|
||||
}
|
||||
|
||||
NodeViewItemWidgetProxy {
|
||||
/* Node styling */
|
||||
NodeViewItemWidget {
|
||||
qproperty-titlebarColor: #4040a0;
|
||||
qproperty-borderColor: #000000;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ QTreeView, QListView, QLineEdit, QMenu, QProgressBar, QPushButton::checked, Node
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
NodeViewItemWidgetProxy {
|
||||
/* Node styling */
|
||||
NodeViewItemWidget {
|
||||
qproperty-titlebarColor: #a0a0ff;
|
||||
}
|
||||
qproperty-borderColor: #000000;
|
||||
}
|
||||
@@ -26,6 +26,8 @@ NodeView::NodeView(QWidget *parent) :
|
||||
{
|
||||
setScene(&scene_);
|
||||
|
||||
setDragMode(RubberBandDrag);
|
||||
|
||||
connect(&scene_, SIGNAL(changed(const QList<QRectF>&)), this, SLOT(ItemsChanged()));
|
||||
}
|
||||
|
||||
@@ -33,7 +35,6 @@ void NodeView::SetGraph(NodeGraph *graph)
|
||||
{
|
||||
// Clear the scene of all UI objects
|
||||
scene_.clear();
|
||||
edges_.clear();
|
||||
|
||||
// Set reference to the graph
|
||||
graph_ = graph;
|
||||
@@ -65,9 +66,6 @@ void NodeView::SetGraph(NodeGraph *graph)
|
||||
edge_ui->SetEdge(edge);
|
||||
|
||||
scene_.addItem(edge_ui);
|
||||
|
||||
// Keep track of edge widgets so they can be quickly updated whenever nodes move
|
||||
edges_.append(edge_ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,9 +90,32 @@ NodeViewItem *NodeView::NodeToUIObject(QGraphicsScene *scene, Node *n)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NodeViewEdge *NodeView::EdgeToUIObject(QGraphicsScene *scene, NodeEdgePtr n)
|
||||
{
|
||||
QList<QGraphicsItem*> graphics_items = scene->items();
|
||||
|
||||
for (int i=0;i<graphics_items.size();i++) {
|
||||
NodeViewEdge* edge = dynamic_cast<NodeViewEdge*>(graphics_items.at(i));
|
||||
|
||||
if (edge != nullptr) {
|
||||
if (edge->edge() == n) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeView::ItemsChanged()
|
||||
{
|
||||
foreach (NodeViewEdge* edge, edges_) {
|
||||
edge->Adjust();
|
||||
QList<QGraphicsItem*> items = scene_.items();
|
||||
|
||||
foreach (QGraphicsItem* item, items) {
|
||||
NodeViewEdge* edge = dynamic_cast<NodeViewEdge*>(item);
|
||||
|
||||
if (edge != nullptr) {
|
||||
edge->Adjust();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +36,13 @@ public:
|
||||
void SetGraph(NodeGraph* graph);
|
||||
|
||||
static NodeViewItem* NodeToUIObject(QGraphicsScene* scene, Node* n);
|
||||
static NodeViewEdge* EdgeToUIObject(QGraphicsScene* scene, NodeEdgePtr n);
|
||||
|
||||
private:
|
||||
NodeGraph* graph_;
|
||||
|
||||
QGraphicsScene scene_;
|
||||
|
||||
QList<NodeViewEdge*> edges_;
|
||||
|
||||
private slots:
|
||||
void ItemsChanged();
|
||||
|
||||
|
||||
@@ -21,21 +21,31 @@
|
||||
#include "nodeviewedge.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
|
||||
#include "common/clamp.h"
|
||||
#include "common/lerp.h"
|
||||
#include "nodeview.h"
|
||||
|
||||
const int kNodeEdgeWidth = 2;
|
||||
|
||||
NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) :
|
||||
QGraphicsLineItem(parent),
|
||||
edge_(nullptr)
|
||||
edge_(nullptr),
|
||||
moving_(false),
|
||||
connected_(false)
|
||||
{
|
||||
// Ensures this UI object is drawn behind other objects
|
||||
setZValue(-1);
|
||||
|
||||
setAcceptHoverEvents(true);
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetEdge(NodeEdgePtr edge)
|
||||
{
|
||||
SetMoving(false);
|
||||
SetConnected(true);
|
||||
|
||||
// Set the new edge pointer
|
||||
edge_ = edge;
|
||||
|
||||
@@ -43,9 +53,32 @@ void NodeViewEdge::SetEdge(NodeEdgePtr edge)
|
||||
Adjust();
|
||||
}
|
||||
|
||||
NodeEdgePtr NodeViewEdge::edge()
|
||||
{
|
||||
return edge_;
|
||||
}
|
||||
|
||||
qreal CalculateEdgeYPoint(NodeViewItem *item, int param_index, NodeViewItem *opposing)
|
||||
{
|
||||
if (item->IsExpanded()) {
|
||||
return item->pos().y() + item->GetParameterConnectorRect(param_index).center().y();
|
||||
} else {
|
||||
qreal max_height = qMax(opposing->rect().height(), item->rect().height());
|
||||
|
||||
// Calculate the Y distance between the two nodes and create a 0.0-1.0 range for lerping
|
||||
qreal input_value = clamp(0.5 + (opposing->pos().y() - item->pos().y()) / max_height / 4, 0.0, 1.0);
|
||||
|
||||
// Use a lerp function to draw the line between the two corners
|
||||
qreal input_y = item->pos().y() + lerp(0.0, item->rect().height(), input_value);
|
||||
|
||||
// Set Y values according to calculations
|
||||
return input_y;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewEdge::Adjust()
|
||||
{
|
||||
if (edge_ == nullptr || scene() == nullptr) {
|
||||
if (edge_ == nullptr || scene() == nullptr || moving_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,28 +101,28 @@ void NodeViewEdge::Adjust()
|
||||
));
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetMoving(bool m)
|
||||
{
|
||||
moving_ = m;
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetConnected(bool c)
|
||||
{
|
||||
connected_ = c;
|
||||
}
|
||||
|
||||
void NodeViewEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
setPen(QPen(widget->palette().color(QPalette::Text), 2));
|
||||
QPalette::ColorGroup color_mode;
|
||||
|
||||
if (connected_) {
|
||||
color_mode = QPalette::Active;
|
||||
} else {
|
||||
color_mode = QPalette::Disabled;
|
||||
}
|
||||
|
||||
setPen(QPen(widget->palette().color(color_mode, QPalette::Text), kNodeEdgeWidth));
|
||||
|
||||
QGraphicsLineItem::paint(painter, option, widget);
|
||||
}
|
||||
|
||||
qreal NodeViewEdge::CalculateEdgeYPoint(NodeViewItem *item, int param_index, NodeViewItem *opposing)
|
||||
{
|
||||
if (item->IsExpanded()) {
|
||||
return item->pos().y() + item->GetParameterConnectorRect(param_index).center().y();
|
||||
} else {
|
||||
qreal max_height = qMax(opposing->rect().height(), item->rect().height());
|
||||
|
||||
// Calculate the Y distance between the two nodes and create a 0.0-1.0 range for lerping
|
||||
qreal input_value = clamp(0.5 + (opposing->pos().y() - item->pos().y()) / max_height / 4, 0.0, 1.0);
|
||||
|
||||
// Use a lerp function to draw the line between the two corners
|
||||
qreal input_y = item->pos().y() + lerp(0.0, item->rect().height(), input_value);
|
||||
|
||||
// Set Y values according to calculations
|
||||
return input_y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <QGraphicsLineItem>
|
||||
|
||||
#include "node/edge.h"
|
||||
#include "nodeviewitem.h"
|
||||
|
||||
class NodeViewEdge : public QGraphicsLineItem
|
||||
{
|
||||
@@ -32,16 +31,21 @@ public:
|
||||
NodeViewEdge(QGraphicsItem* parent = nullptr);
|
||||
|
||||
void SetEdge(NodeEdgePtr edge);
|
||||
NodeEdgePtr edge();
|
||||
|
||||
void Adjust();
|
||||
|
||||
void SetMoving(bool m);
|
||||
void SetConnected(bool c);
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
private:
|
||||
qreal CalculateEdgeYPoint(NodeViewItem* item, int param_index, NodeViewItem* opposing);
|
||||
|
||||
NodeEdgePtr edge_;
|
||||
|
||||
bool moving_;
|
||||
bool connected_;
|
||||
};
|
||||
|
||||
#endif // NODEEDGEITEM_H
|
||||
|
||||
@@ -21,16 +21,18 @@
|
||||
#include "nodeviewitem.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
|
||||
#include "core.h"
|
||||
#include "nodeview.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
const int kNodeViewItemBorderWidth = 2;
|
||||
const int kNodeViewItemWidth = 250;
|
||||
const int kNodeViewItemWidth = 200;
|
||||
const int kNodeViewItemTextPadding = 4;
|
||||
const int kNodeViewItemIconPadding = 12;
|
||||
|
||||
@@ -38,6 +40,7 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
node_(nullptr),
|
||||
font_metrics(font),
|
||||
dragging_edge_(nullptr),
|
||||
expanded_(false),
|
||||
standard_click_(false)
|
||||
{
|
||||
@@ -144,8 +147,7 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
|
||||
QPalette app_pal = olive::core.main_window()->palette();
|
||||
|
||||
// Set up border, which will change color if selected
|
||||
// FIXME: Color not configurable?
|
||||
QPen border_pen(Qt::black, kNodeViewItemBorderWidth);
|
||||
QPen border_pen(obj_proxy_.BorderColor(), kNodeViewItemBorderWidth);
|
||||
|
||||
QPen text_pen(app_pal.color(QPalette::Text));
|
||||
|
||||
@@ -254,9 +256,50 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
if (IsExpanded() // This is only possible if the node is expanded
|
||||
&& node_ != nullptr) { // We can only loop through a node's parameters if a valid node is attached
|
||||
for (int i=0;i<node_->ParameterCount();i++) {
|
||||
if (GetParameterConnectorRect(i).contains(event->pos())) {
|
||||
|
||||
if (GetParameterConnectorRect(i).contains(event->pos())) { // See if the cursor is in the rect
|
||||
|
||||
NodeParam* param = node_->ParamAt(i);
|
||||
|
||||
if (param->type() == NodeParam::kOutput || param->edges().isEmpty()) {
|
||||
// For an output param (or an input param with no connections), we default to creating a new edge
|
||||
|
||||
// Create new NodeViewEdge object for the user to create an edge
|
||||
dragging_edge_ = new NodeViewEdge();
|
||||
dragging_edge_->SetMoving(true);
|
||||
|
||||
drag_source_ = this;
|
||||
drag_src_param_ = param;
|
||||
|
||||
// Set the starting position to the current param's connector
|
||||
dragging_edge_start_ = pos() + GetParameterConnectorRect(i).center();
|
||||
dragging_edge_->setLine(QLineF(dragging_edge_start_, dragging_edge_start_));
|
||||
|
||||
// Add it to the scene
|
||||
scene()->addItem(dragging_edge_);
|
||||
|
||||
} else if (param->type() == NodeParam::kInput) {
|
||||
// For an input param, we default to moving an existing edge
|
||||
// (here we use the last one, which will usually also be the first)
|
||||
NodeEdgePtr edge = param->edges().last();
|
||||
|
||||
dragging_edge_ = NodeView::EdgeToUIObject(scene(), edge);
|
||||
dragging_edge_->SetMoving(true);
|
||||
|
||||
// The starting position will be the OPPOSING param's rect
|
||||
NodeOutput* opposing_param = edge->output();
|
||||
Node* opposing_node = opposing_param->parent();
|
||||
NodeViewItem* opposing_node_view_item = NodeView::NodeToUIObject(scene(), opposing_node);
|
||||
dragging_edge_start_ = opposing_node_view_item->pos() + opposing_node_view_item->GetParameterConnectorRect(opposing_param->index()).center();
|
||||
|
||||
drag_source_ = opposing_node_view_item;
|
||||
drag_src_param_ = opposing_param;
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +310,45 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
|
||||
void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
// Check if an edge drag was initiated
|
||||
if (dragging_edge_ != nullptr) {
|
||||
QPointF end_point = event->scenePos();
|
||||
drag_dest_param_ = nullptr;
|
||||
|
||||
// See if the mouse is currently inside a node
|
||||
NodeViewItem* drop_item = dynamic_cast<NodeViewItem*>(scene()->itemAt(event->scenePos(), sceneTransform()));
|
||||
if (drop_item != nullptr && drop_item != drag_source_) {
|
||||
if (drop_item->IsExpanded()) {
|
||||
drop_item->SetExpanded(true);
|
||||
}
|
||||
|
||||
// See if the mouse is currently inside a connector rect
|
||||
for (int i=0;i<drop_item->node()->ParameterCount();i++) {
|
||||
QRectF comp_rect = drop_item->GetParameterConnectorRect(i).adjusted(-node_connector_size_,
|
||||
-node_connector_size_,
|
||||
node_connector_size_,
|
||||
node_connector_size_);
|
||||
|
||||
NodeParam* comp_param = drop_item->node()->ParamAt(i);
|
||||
|
||||
// If so, we snap inside it
|
||||
if (comp_rect.contains(drop_item->mapFromScene(event->scenePos()))) {
|
||||
|
||||
drag_dest_param_ = comp_param;
|
||||
end_point = drop_item->mapToScene(drop_item->GetParameterConnectorRect(i).center());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dragging_edge_->SetConnected(drag_dest_param_ != nullptr);
|
||||
|
||||
dragging_edge_->setLine(QLineF(dragging_edge_start_, end_point));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (standard_click_) {
|
||||
QGraphicsRectItem::mouseMoveEvent(event);
|
||||
}
|
||||
@@ -274,6 +356,38 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
|
||||
void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
// Check if an edge drag was initiated
|
||||
if (dragging_edge_ != nullptr) {
|
||||
|
||||
// FIXME: Make this undoable
|
||||
|
||||
// If this edge had an edge, we should disconnect it now
|
||||
if (dragging_edge_->edge() != nullptr) {
|
||||
NodeParam::DisconnectEdge(dragging_edge_->edge());
|
||||
|
||||
dragging_edge_->SetEdge(nullptr);
|
||||
}
|
||||
|
||||
if (drag_dest_param_ == nullptr) {
|
||||
// If we didn't drag to anywhere, just get rid of this edge
|
||||
scene()->removeItem(dragging_edge_);
|
||||
} else {
|
||||
// If we did, create a new edge now
|
||||
NodeEdgePtr new_edge;
|
||||
|
||||
if (drag_dest_param_->type() == NodeParam::kOutput) {
|
||||
new_edge = NodeParam::ConnectEdge(static_cast<NodeOutput*>(drag_dest_param_), static_cast<NodeInput*>(drag_src_param_));
|
||||
} else {
|
||||
new_edge = NodeParam::ConnectEdge(static_cast<NodeOutput*>(drag_src_param_), static_cast<NodeInput*>(drag_dest_param_));
|
||||
}
|
||||
|
||||
dragging_edge_->SetEdge(new_edge);
|
||||
}
|
||||
|
||||
dragging_edge_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we clicked the Expand/Collapse icon
|
||||
if (expand_hitbox_.contains(event->pos())) {
|
||||
SetExpanded(!IsExpanded());
|
||||
@@ -284,16 +398,26 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
NodeViewItemWidgetProxy::NodeViewItemWidgetProxy()
|
||||
NodeViewItemWidget::NodeViewItemWidget()
|
||||
{
|
||||
}
|
||||
|
||||
QColor NodeViewItemWidgetProxy::TitleBarColor()
|
||||
QColor NodeViewItemWidget::TitleBarColor()
|
||||
{
|
||||
return title_bar_color_;
|
||||
}
|
||||
|
||||
void NodeViewItemWidgetProxy::SetTitleBarColor(QColor color)
|
||||
void NodeViewItemWidget::SetTitleBarColor(QColor color)
|
||||
{
|
||||
title_bar_color_ = color;
|
||||
}
|
||||
|
||||
QColor NodeViewItemWidget::BorderColor()
|
||||
{
|
||||
return border_color_;
|
||||
}
|
||||
|
||||
void NodeViewItemWidget::SetBorderColor(QColor color)
|
||||
{
|
||||
border_color_ = color;
|
||||
}
|
||||
|
||||
@@ -27,17 +27,26 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include "node/node.h"
|
||||
#include "nodeviewedge.h"
|
||||
|
||||
class NodeViewItemWidgetProxy : public QWidget {
|
||||
/**
|
||||
* @brief A proxy object to allow NodeViewItem access to CSS functions
|
||||
*/
|
||||
class NodeViewItemWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor titlebarColor READ TitleBarColor WRITE SetTitleBarColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor borderColor READ BorderColor WRITE SetBorderColor DESIGNABLE true)
|
||||
public:
|
||||
NodeViewItemWidgetProxy();
|
||||
NodeViewItemWidget();
|
||||
|
||||
QColor TitleBarColor();
|
||||
void SetTitleBarColor(QColor color);
|
||||
|
||||
QColor BorderColor();
|
||||
void SetBorderColor(QColor color);
|
||||
private:
|
||||
QColor title_bar_color_;
|
||||
QColor border_color_;
|
||||
};
|
||||
|
||||
class NodeViewItem : public QGraphicsRectItem
|
||||
@@ -67,7 +76,7 @@ private:
|
||||
|
||||
Node* node_;
|
||||
|
||||
NodeViewItemWidgetProxy obj_proxy_;
|
||||
NodeViewItemWidget obj_proxy_;
|
||||
|
||||
QRectF title_bar_rect_;
|
||||
|
||||
@@ -77,6 +86,13 @@ private:
|
||||
|
||||
QFontMetrics font_metrics;
|
||||
|
||||
// Edge dragging variables
|
||||
NodeViewEdge* dragging_edge_;
|
||||
QPointF dragging_edge_start_;
|
||||
NodeParam* drag_src_param_;
|
||||
NodeParam* drag_dest_param_;
|
||||
NodeViewItem* drag_source_;
|
||||
|
||||
int node_connector_size_;
|
||||
|
||||
bool expanded_;
|
||||
|
||||
Reference in New Issue
Block a user