nodes can now be connected through the node editor
This commit is contained in:
+56
-1
@@ -65,13 +65,54 @@ void EffectRow::AddField(EffectField *field)
|
||||
fields_.append(field);
|
||||
}
|
||||
|
||||
void EffectRow::AddNodeInput(olive::nodes::DataType type)
|
||||
void EffectRow::AddAcceptedNodeInput(olive::nodes::DataType type)
|
||||
{
|
||||
Q_ASSERT(output_type_ == olive::nodes::kInvalid);
|
||||
|
||||
accepted_inputs_.append(type);
|
||||
}
|
||||
|
||||
void EffectRow::ConnectEdge(EffectRow *output, EffectRow *input)
|
||||
{
|
||||
Q_ASSERT(output->IsNodeOutput());
|
||||
Q_ASSERT(input->IsNodeInput());
|
||||
|
||||
if (!input->node_edges_.isEmpty()) {
|
||||
DisconnectEdge(input->node_edges_.first());
|
||||
}
|
||||
|
||||
NodeEdgePtr edge = std::make_shared<NodeEdge>(output, input);
|
||||
|
||||
output->node_edges_.append(edge);
|
||||
input->node_edges_.append(edge);
|
||||
|
||||
emit output->EdgesChanged();
|
||||
emit input->EdgesChanged();
|
||||
}
|
||||
|
||||
void EffectRow::DisconnectEdge(NodeEdgePtr edge)
|
||||
{
|
||||
EffectRow* output = edge->output();
|
||||
EffectRow* input = edge->input();
|
||||
|
||||
output->node_edges_.removeAll(edge);
|
||||
input->node_edges_.removeAll(edge);
|
||||
|
||||
emit output->EdgesChanged();
|
||||
emit input->EdgesChanged();
|
||||
}
|
||||
|
||||
QVector<NodeEdge*> EffectRow::edges()
|
||||
{
|
||||
QVector<NodeEdge*> edges;
|
||||
|
||||
for (int i=0;i<node_edges_.size();i++) {
|
||||
edges.append(node_edges_.at(i).get());
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
bool EffectRow::IsKeyframing() {
|
||||
return keyframing_;
|
||||
}
|
||||
@@ -121,6 +162,20 @@ void EffectRow::SetOutputDataType(olive::nodes::DataType type)
|
||||
output_type_ = type;
|
||||
}
|
||||
|
||||
bool EffectRow::CanAcceptDataType(olive::nodes::DataType type)
|
||||
{
|
||||
if (!IsNodeInput()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return accepted_inputs_.contains(type);
|
||||
}
|
||||
|
||||
olive::nodes::DataType EffectRow::OutputDataType()
|
||||
{
|
||||
return output_type_;
|
||||
}
|
||||
|
||||
bool EffectRow::IsNodeInput()
|
||||
{
|
||||
return !accepted_inputs_.isEmpty();
|
||||
|
||||
+71
-12
@@ -35,6 +35,7 @@ class KeyframeNavigator;
|
||||
class ClickableLabel;
|
||||
|
||||
#include "effectfields.h"
|
||||
#include "nodes/nodeedge.h"
|
||||
|
||||
/**
|
||||
* @brief The EffectRow class
|
||||
@@ -245,6 +246,64 @@ public:
|
||||
*/
|
||||
void SetOutputDataType(olive::nodes::DataType type);
|
||||
|
||||
/**
|
||||
* @brief Check if this row can accept the data type specified in type
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* TRUE if this row can accept this data type. If this row is not an input, this function will always return FALSE.
|
||||
*/
|
||||
bool CanAcceptDataType(olive::nodes::DataType type);
|
||||
|
||||
/**
|
||||
* @brief Get this row's output data type
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* If this is not an output, this will always return olive::nodes::kInvalid
|
||||
*/
|
||||
olive::nodes::DataType OutputDataType();
|
||||
|
||||
/**
|
||||
* @brief Adds a node data type that can be accepted by this input
|
||||
*
|
||||
* Allows this input to take a node connection from a data type specified by type. An input can take several data
|
||||
* types.
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* The data type to add
|
||||
*/
|
||||
void AddAcceptedNodeInput(olive::nodes::DataType type);
|
||||
|
||||
/**
|
||||
* @brief Connect two node sockets together
|
||||
*
|
||||
* For outputs, the maximum amount of edges is unlimited and this will continually add to a list of edges. For inputs,
|
||||
* there can only be one edge and adding a second will destroy the first.
|
||||
*
|
||||
* @param edge
|
||||
*
|
||||
* The edge to add (output) or replace the current edge (input).
|
||||
*/
|
||||
static void ConnectEdge(EffectRow* output, EffectRow* input);
|
||||
|
||||
/**
|
||||
* @brief Disconnect an edge
|
||||
*
|
||||
* Disconnects two nodes and destroys the edge object connecting them together
|
||||
*
|
||||
* @param edge
|
||||
*
|
||||
* Edge to be removed
|
||||
*/
|
||||
static void DisconnectEdge(NodeEdgePtr edge);
|
||||
|
||||
/**
|
||||
* @brief Get a list of references to all edges currently connected to this row
|
||||
*/
|
||||
QVector<NodeEdge *> edges();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Add a field to this row
|
||||
@@ -258,18 +317,6 @@ protected:
|
||||
*/
|
||||
void AddField(EffectField* Field);
|
||||
|
||||
/**
|
||||
* @brief Adds a node data type that can be accepted by this input
|
||||
*
|
||||
* Allows this input to take a node connection from a data type specified by type. An input can take several data
|
||||
* types.
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* The data type to add
|
||||
*/
|
||||
void AddNodeInput(olive::nodes::DataType type);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Go to previous keyframe
|
||||
@@ -332,6 +379,13 @@ signals:
|
||||
*/
|
||||
void Clicked();
|
||||
|
||||
/**
|
||||
* @brief Edges changed signal
|
||||
*
|
||||
* Signal emitted any time an edge is connected or disconnected from this row
|
||||
*/
|
||||
void EdgesChanged();
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Set keyframing enabled state
|
||||
@@ -402,6 +456,11 @@ private:
|
||||
* this.
|
||||
*/
|
||||
olive::nodes::DataType output_type_;
|
||||
|
||||
/**
|
||||
* @brief Internal array of node edges. Access with AddEdge() and RemoveEdge().
|
||||
*/
|
||||
QVector<NodeEdgePtr> node_edges_;
|
||||
};
|
||||
|
||||
#endif // EFFECTROW_H
|
||||
|
||||
@@ -7,7 +7,7 @@ BoolInput::BoolInput(Node* parent, const QString& id, const QString& name, bool
|
||||
connect(bool_field, SIGNAL(Toggled(bool)), this, SIGNAL(Toggled(bool)));
|
||||
AddField(bool_field);
|
||||
|
||||
AddNodeInput(olive::nodes::kBoolean);
|
||||
AddAcceptedNodeInput(olive::nodes::kBoolean);
|
||||
}
|
||||
|
||||
bool BoolInput::GetBoolAt(double timecode)
|
||||
|
||||
@@ -5,7 +5,7 @@ ColorInput::ColorInput(Node* parent, const QString& id, const QString& name, boo
|
||||
{
|
||||
AddField(new ColorField(this));
|
||||
|
||||
AddNodeInput(olive::nodes::kColor);
|
||||
AddAcceptedNodeInput(olive::nodes::kColor);
|
||||
}
|
||||
|
||||
QColor ColorInput::GetColorAt(double timecode)
|
||||
|
||||
@@ -7,7 +7,7 @@ ComboInput::ComboInput(Node* parent, const QString& id, const QString& name, boo
|
||||
connect(combo_field, SIGNAL(DataChanged(const QVariant&)), this, SIGNAL(DataChanged(const QVariant&)));
|
||||
AddField(combo_field);
|
||||
|
||||
AddNodeInput(olive::nodes::kCombo);
|
||||
AddAcceptedNodeInput(olive::nodes::kCombo);
|
||||
}
|
||||
|
||||
void ComboInput::AddItem(const QString &text, const QVariant &data)
|
||||
|
||||
@@ -5,7 +5,7 @@ FileInput::FileInput(Node* parent, const QString& id, const QString& name, bool
|
||||
{
|
||||
AddField(new FileField(this));
|
||||
|
||||
AddNodeInput(olive::nodes::kFile);
|
||||
AddAcceptedNodeInput(olive::nodes::kFile);
|
||||
}
|
||||
|
||||
QString FileInput::GetFileAt(double timecode)
|
||||
|
||||
@@ -5,7 +5,7 @@ FontInput::FontInput(Node* parent, const QString& id, const QString& name, bool
|
||||
{
|
||||
AddField(new FontField(this));
|
||||
|
||||
AddNodeInput(olive::nodes::kFont);
|
||||
AddAcceptedNodeInput(olive::nodes::kFont);
|
||||
}
|
||||
|
||||
QString FontInput::GetFontAt(double timecode)
|
||||
|
||||
@@ -5,7 +5,7 @@ StringInput::StringInput(Node* parent, const QString& id, const QString& name, b
|
||||
{
|
||||
AddField(new StringField(this, rich_text));
|
||||
|
||||
AddNodeInput(olive::nodes::kString);
|
||||
AddAcceptedNodeInput(olive::nodes::kString);
|
||||
}
|
||||
|
||||
QString StringInput::GetStringAt(double timecode)
|
||||
|
||||
@@ -15,15 +15,15 @@ VecInput::VecInput(Node* parent, const QString& id, const QString& name, int val
|
||||
AddField(new DoubleField(this));
|
||||
}
|
||||
|
||||
AddNodeInput(olive::nodes::kFloat);
|
||||
AddAcceptedNodeInput(olive::nodes::kFloat);
|
||||
if (values > 1) {
|
||||
AddNodeInput(olive::nodes::kVec2);
|
||||
AddAcceptedNodeInput(olive::nodes::kVec2);
|
||||
}
|
||||
if (values > 2) {
|
||||
AddNodeInput(olive::nodes::kVec3);
|
||||
AddAcceptedNodeInput(olive::nodes::kVec3);
|
||||
}
|
||||
if (values > 3) {
|
||||
AddNodeInput(olive::nodes::kVec4);
|
||||
AddAcceptedNodeInput(olive::nodes::kVec4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -148,6 +148,11 @@ void Node::AddRow(EffectRow *row)
|
||||
rows.append(row);
|
||||
}
|
||||
|
||||
int Node::IndexOfRow(EffectRow *row)
|
||||
{
|
||||
return rows.indexOf(row);
|
||||
}
|
||||
|
||||
void Node::copy_field_keyframes(NodePtr e) {
|
||||
for (int i=0;i<rows.size();i++) {
|
||||
EffectRow* row = rows.at(i);
|
||||
@@ -191,6 +196,17 @@ int Node::gizmo_count() {
|
||||
return gizmos.size();
|
||||
}
|
||||
|
||||
QVector<NodeEdge *> Node::GetAllEdges()
|
||||
{
|
||||
QVector<NodeEdge*> edges;
|
||||
|
||||
for (int i=0;i<row_count();i++) {
|
||||
edges.append(row(i)->edges());
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
void Node::refresh() {}
|
||||
|
||||
void Node::FieldChanged() {
|
||||
@@ -310,6 +326,11 @@ void Node::SetExpanded(bool e)
|
||||
expanded_ = e;
|
||||
}
|
||||
|
||||
void Node::SetPos(const QPointF &pos)
|
||||
{
|
||||
pos_ = pos;
|
||||
}
|
||||
|
||||
void Node::SetEnabled(bool b) {
|
||||
enabled_ = b;
|
||||
emit EnabledChanged(b);
|
||||
@@ -615,6 +636,11 @@ void Node::setIterations(int i) {
|
||||
iterations = i;
|
||||
}
|
||||
|
||||
const QPointF &Node::pos()
|
||||
{
|
||||
return pos_;
|
||||
}
|
||||
|
||||
void Node::process_image(double, uint8_t *, uint8_t *, int){}
|
||||
|
||||
NodePtr Node::copy(Clip *c) {
|
||||
|
||||
+8
-1
@@ -127,7 +127,7 @@ public:
|
||||
virtual NodePtr Create(Clip *c) = 0;
|
||||
|
||||
void AddRow(EffectRow* row);
|
||||
|
||||
int IndexOfRow(EffectRow* row);
|
||||
EffectRow* row(int i);
|
||||
int row_count();
|
||||
|
||||
@@ -135,6 +135,8 @@ public:
|
||||
EffectGizmo* gizmo(int i);
|
||||
int gizmo_count();
|
||||
|
||||
QVector<NodeEdge*> GetAllEdges();
|
||||
|
||||
bool IsEnabled();
|
||||
bool IsExpanded();
|
||||
|
||||
@@ -168,6 +170,8 @@ public:
|
||||
int getIterations();
|
||||
void setIterations(int i);
|
||||
|
||||
const QPointF& pos();
|
||||
|
||||
virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size);
|
||||
virtual void process_shader(double timecode, GLTextureCoords&, int iteration);
|
||||
virtual void process_coords(double timecode, GLTextureCoords& coords, int data);
|
||||
@@ -225,6 +229,7 @@ public slots:
|
||||
void FieldChanged();
|
||||
void SetEnabled(bool b);
|
||||
void SetExpanded(bool e);
|
||||
void SetPos(const QPointF& pos);
|
||||
signals:
|
||||
void EnabledChanged(bool);
|
||||
private slots:
|
||||
@@ -264,6 +269,8 @@ private:
|
||||
|
||||
QVector<KeyframeDataChange*> gizmo_dragging_actions_;
|
||||
|
||||
QPointF pos_;
|
||||
|
||||
// superimpose functions
|
||||
virtual void redraw(double timecode);
|
||||
bool valueHasChanged(double timecode);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "nodeedge.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "effects/effectrow.h"
|
||||
|
||||
NodeEdge::NodeEdge(EffectRow *output, EffectRow *input) :
|
||||
output_(output),
|
||||
input_(input)
|
||||
{
|
||||
}
|
||||
|
||||
EffectRow *NodeEdge::output()
|
||||
{
|
||||
return output_;
|
||||
}
|
||||
|
||||
EffectRow *NodeEdge::input()
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef NODEEDGE_H
|
||||
#define NODEEDGE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
class EffectRow;
|
||||
|
||||
class NodeEdge;
|
||||
using NodeEdgePtr = std::shared_ptr<NodeEdge>;
|
||||
|
||||
class NodeEdge
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief NodeEdge Constructor
|
||||
*
|
||||
* Creates a node edge and stores its two connections.
|
||||
*
|
||||
* This should not be used directly. Use the static function EffectRow::ConnectEdge instead.
|
||||
*
|
||||
* @param output
|
||||
*
|
||||
* Output/from EffectRow that this edge "starts" at
|
||||
*
|
||||
* @param input
|
||||
*
|
||||
* Input/to EffectRow that this edge "ends" at
|
||||
*/
|
||||
NodeEdge(EffectRow* output, EffectRow* input);
|
||||
|
||||
EffectRow* output();
|
||||
EffectRow* input();
|
||||
|
||||
private:
|
||||
EffectRow* output_;
|
||||
EffectRow* input_;
|
||||
};
|
||||
|
||||
#endif // NODEEDGE_H
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "nodeplug.h"
|
||||
|
||||
NodePlug::NodePlug()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool NodePlug::IsConnected()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#ifndef NODEPLUG_H
|
||||
#define NODEPLUG_H
|
||||
|
||||
|
||||
class NodePlug
|
||||
{
|
||||
public:
|
||||
NodePlug();
|
||||
|
||||
bool IsConnected();
|
||||
};
|
||||
|
||||
#endif // NODEPLUG_H
|
||||
@@ -3,6 +3,8 @@
|
||||
NodeMedia::NodeMedia(Clip* c) :
|
||||
Node(c)
|
||||
{
|
||||
EffectRow* matrix_input = new EffectRow(this, "matrix", tr("Matrix"), true, false);
|
||||
matrix_input->AddAcceptedNodeInput(olive::nodes::kMatrix);
|
||||
}
|
||||
|
||||
QString NodeMedia::name()
|
||||
|
||||
@@ -191,7 +191,6 @@ SOURCES += \
|
||||
ui/nodeui.cpp \
|
||||
panels/effectspanel.cpp \
|
||||
nodes/nodedatatypes.cpp \
|
||||
nodes/nodeplug.cpp \
|
||||
nodes/inputs/boolinput.cpp \
|
||||
nodes/inputs/comboinput.cpp \
|
||||
nodes/inputs/colorinput.cpp \
|
||||
@@ -207,7 +206,9 @@ SOURCES += \
|
||||
nodes/nodes/nodeshader.cpp \
|
||||
decoders/ffmpegdecoder.cpp \
|
||||
decoders/decoder.cpp \
|
||||
nodes/node.cpp
|
||||
nodes/node.cpp \
|
||||
nodes/nodeedge.cpp \
|
||||
ui/nodeedgeui.cpp
|
||||
|
||||
HEADERS += \
|
||||
ui/mainwindow.h \
|
||||
@@ -353,7 +354,6 @@ HEADERS += \
|
||||
ui/nodeui.h \
|
||||
panels/effectspanel.h \
|
||||
nodes/nodedatatypes.h \
|
||||
nodes/nodeplug.h \
|
||||
nodes/inputs.h \
|
||||
nodes/inputs/boolinput.h \
|
||||
nodes/inputs/comboinput.h \
|
||||
@@ -372,7 +372,9 @@ HEADERS += \
|
||||
decoders/ffmpegdecoder.h \
|
||||
decoders/decoder.h \
|
||||
nodes/node.h \
|
||||
timeline/tracktypes.h
|
||||
timeline/tracktypes.h \
|
||||
nodes/nodeedge.h \
|
||||
ui/nodeedgeui.h
|
||||
|
||||
FORMS +=
|
||||
|
||||
|
||||
+42
-7
@@ -24,6 +24,8 @@ NodeEditor::NodeEditor(QWidget *parent) :
|
||||
|
||||
view_.setInteractive(true);
|
||||
view_.setDragMode(QGraphicsView::RubberBandDrag);
|
||||
|
||||
connect(&scene_, SIGNAL(changed(const QList<QRectF>&)), this, SLOT(ItemsChanged()));
|
||||
}
|
||||
|
||||
void NodeEditor::Retranslate()
|
||||
@@ -32,9 +34,9 @@ void NodeEditor::Retranslate()
|
||||
|
||||
void NodeEditor::LoadEvent()
|
||||
{
|
||||
nodes_.resize(open_effects_.size());
|
||||
|
||||
if (!open_effects_.isEmpty()) {
|
||||
QVector<NodeEdge*> added_edges;
|
||||
|
||||
Clip* first_clip = open_effects_.first()->GetEffect()->parent_clip;
|
||||
|
||||
for (int i=0;i<open_effects_.size();i++) {
|
||||
@@ -48,20 +50,53 @@ void NodeEditor::LoadEvent()
|
||||
|
||||
node_ui->SetWidget(effect_ui);
|
||||
node_ui->AddToScene(&scene_);
|
||||
node_ui->setPos(effect_ui->GetEffect()->pos());
|
||||
|
||||
nodes_[i] = node_ui;
|
||||
nodes_.append(node_ui);
|
||||
|
||||
// Get node edges
|
||||
QVector<NodeEdge*> edges = open_effects_.at(i)->GetEffect()->GetAllEdges();
|
||||
for (int j=0;j<edges.size();j++) {
|
||||
|
||||
NodeEdge* edge = edges.at(j);
|
||||
|
||||
if (!added_edges.contains(edge)) {
|
||||
NodeEdgeUI* edge_ui = new NodeEdgeUI(edge);
|
||||
scene_.addItem(edge_ui);
|
||||
edges_.append(edge_ui);
|
||||
|
||||
added_edges.append(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ItemsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeEditor::ClearEvent()
|
||||
{
|
||||
NodeUI* node;
|
||||
|
||||
foreach (node, nodes_) {
|
||||
delete node;
|
||||
foreach (NodeUI* node, nodes_) {
|
||||
scene_.removeItem(node);
|
||||
}
|
||||
|
||||
nodes_.clear();
|
||||
|
||||
foreach (NodeEdgeUI* edge, edges_) {
|
||||
scene_.removeItem(edge);
|
||||
}
|
||||
|
||||
edges_.clear();
|
||||
}
|
||||
|
||||
void NodeEditor::ItemsChanged()
|
||||
{
|
||||
foreach (NodeEdgeUI* edge, edges_) {
|
||||
edge->adjust();
|
||||
}
|
||||
|
||||
foreach (NodeUI* node, nodes_) {
|
||||
node->Widget()->GetEffect()->SetPos(node->pos());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "effectspanel.h"
|
||||
#include "ui/nodeview.h"
|
||||
#include "ui/nodeui.h"
|
||||
#include "ui/nodeedgeui.h"
|
||||
|
||||
class NodeEditor : public EffectsPanel {
|
||||
Q_OBJECT
|
||||
@@ -22,6 +23,10 @@ private:
|
||||
QGraphicsScene scene_;
|
||||
NodeView view_;
|
||||
QVector<NodeUI*> nodes_;
|
||||
QVector<NodeEdgeUI*> edges_;
|
||||
|
||||
private slots:
|
||||
void ItemsChanged();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "nodeedgeui.h"
|
||||
|
||||
#include <QPen>
|
||||
#include <QGraphicsScene>
|
||||
|
||||
#include "global/math.h"
|
||||
#include "ui/effectui.h"
|
||||
|
||||
NodeEdgeUI::NodeEdgeUI(NodeEdge* edge) :
|
||||
edge_(edge),
|
||||
output_node_(nullptr),
|
||||
input_node_(nullptr)
|
||||
{
|
||||
setPen(QPen(Qt::white, 2));
|
||||
}
|
||||
|
||||
void NodeEdgeUI::adjust()
|
||||
{
|
||||
if (output_node_ == nullptr) {
|
||||
// Locate the output and input nodes' UIs
|
||||
|
||||
QList<QGraphicsItem*> all_items = scene()->items();
|
||||
|
||||
for (int j=0;j<all_items.size();j++) {
|
||||
NodeUI* node = dynamic_cast<NodeUI*>(all_items.at(j));
|
||||
if (node != nullptr) {
|
||||
|
||||
// Check if this node has the output row
|
||||
int row_index = node->Widget()->GetEffect()->IndexOfRow(edge_->output());
|
||||
|
||||
if (row_index > -1) {
|
||||
output_node_ = node;
|
||||
output_index_ = row_index;
|
||||
}
|
||||
|
||||
// Check if this node has the input row
|
||||
row_index = node->Widget()->GetEffect()->IndexOfRow(edge_->input());
|
||||
|
||||
if (row_index > -1) {
|
||||
input_node_ = node;
|
||||
input_index_ = row_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setPath(GetEdgePath(output_node_->pos() + output_node_->GetNodeSocketRects().at(output_index_).center(),
|
||||
input_node_->pos() + input_node_->GetNodeSocketRects().at(input_index_).center()));
|
||||
}
|
||||
|
||||
NodeEdge *NodeEdgeUI::edge()
|
||||
{
|
||||
return edge_;
|
||||
}
|
||||
|
||||
QPainterPath NodeEdgeUI::GetEdgePath(const QPointF &start_pos, const QPointF &end_pos)
|
||||
{
|
||||
double mid_x = double_lerp(start_pos.x(), end_pos.x(), 0.5);
|
||||
|
||||
QPainterPath path_;
|
||||
path_.moveTo(start_pos);
|
||||
path_.cubicTo(QPointF(mid_x, start_pos.y()),
|
||||
QPointF(mid_x, end_pos.y()),
|
||||
end_pos);
|
||||
|
||||
return path_;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef NODEEDGEUI_H
|
||||
#define NODEEDGEUI_H
|
||||
|
||||
#include <QGraphicsPathItem>
|
||||
|
||||
#include "nodeui.h"
|
||||
|
||||
class NodeEdgeUI : public QGraphicsPathItem {
|
||||
public:
|
||||
NodeEdgeUI(NodeEdge* edge);
|
||||
|
||||
static QPainterPath GetEdgePath(const QPointF& start_pos, const QPointF& end_pos);
|
||||
|
||||
void adjust();
|
||||
NodeEdge* edge();
|
||||
|
||||
private:
|
||||
NodeEdge* edge_;
|
||||
|
||||
NodeUI* output_node_;
|
||||
int output_index_;
|
||||
NodeUI* input_node_;
|
||||
int input_index_;
|
||||
};
|
||||
|
||||
#endif // NODEEDGEUI_H
|
||||
+87
-22
@@ -12,13 +12,15 @@
|
||||
#include <QPainter>
|
||||
|
||||
#include "ui/effectui.h"
|
||||
#include "global/math.h"
|
||||
#include "ui/nodeedgeui.h"
|
||||
|
||||
const int kRoundedRectRadius = 5;
|
||||
const int kNodePlugSize = 12;
|
||||
|
||||
NodeUI::NodeUI() :
|
||||
central_widget_(nullptr),
|
||||
proxy_(nullptr),
|
||||
drag_destination_(nullptr),
|
||||
clicked_socket_(-1)
|
||||
{
|
||||
setFlag(QGraphicsItem::ItemIsMovable, true);
|
||||
@@ -29,7 +31,6 @@ NodeUI::~NodeUI()
|
||||
{
|
||||
if (scene() != nullptr) {
|
||||
scene()->removeItem(proxy_);
|
||||
scene()->removeItem(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +56,8 @@ void NodeUI::Resize(const QSize &s)
|
||||
|
||||
rectangle.setWidth(rectangle.width() + kNodePlugSize);
|
||||
|
||||
path_ = QPainterPath();
|
||||
path_.addRoundedRect(inner_rect, kRoundedRectRadius, kRoundedRectRadius);
|
||||
drag_path_ = QPainterPath();
|
||||
drag_path_.addRoundedRect(inner_rect, kRoundedRectRadius, kRoundedRectRadius);
|
||||
|
||||
setRect(rectangle);
|
||||
}
|
||||
@@ -66,6 +67,11 @@ void NodeUI::SetWidget(EffectUI *widget)
|
||||
central_widget_ = widget;
|
||||
}
|
||||
|
||||
EffectUI *NodeUI::Widget()
|
||||
{
|
||||
return central_widget_;
|
||||
}
|
||||
|
||||
void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(widget)
|
||||
@@ -88,7 +94,7 @@ void NodeUI::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QW
|
||||
painter->setPen(palette.base().color());
|
||||
}
|
||||
painter->setBrush(palette.window());
|
||||
painter->drawPath(path_);
|
||||
painter->drawPath(drag_path_);
|
||||
}
|
||||
|
||||
void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
@@ -96,6 +102,7 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
QVector<QRectF> sockets = GetNodeSocketRects();
|
||||
|
||||
clicked_socket_ = -1;
|
||||
drag_destination_ = nullptr;
|
||||
|
||||
for (int i=0;i<sockets.size();i++) {
|
||||
if (sockets.at(i).contains(event->pos())) {
|
||||
@@ -106,8 +113,7 @@ void NodeUI::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
|
||||
if (clicked_socket_ > -1) {
|
||||
drag_line_start_ = pos() + sockets.at(clicked_socket_).center();
|
||||
drag_line_ = scene()->addPath(GetEdgePath(drag_line_start_, event->scenePos()),
|
||||
QPen(Qt::white, 2));
|
||||
drag_line_ = scene()->addPath(NodeEdgeUI::GetEdgePath(drag_line_start_, event->scenePos()));
|
||||
|
||||
event->accept();
|
||||
} else {
|
||||
@@ -119,7 +125,65 @@ void NodeUI::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (clicked_socket_ > -1) {
|
||||
|
||||
drag_line_->setPath(GetEdgePath(drag_line_start_, event->scenePos()));
|
||||
bool line_is_touching_node = false;
|
||||
|
||||
QPointF drag_line_end = event->scenePos();
|
||||
|
||||
// Get all the graphics items at this mouse position
|
||||
QList<QGraphicsItem *> mouse_items = scene()->items(event->scenePos());
|
||||
for (int j=0;j<mouse_items.size();j++) {
|
||||
|
||||
// See if the graphics item here is a node
|
||||
NodeUI* mouse_node = dynamic_cast<NodeUI*>(mouse_items.at(j));
|
||||
|
||||
if (mouse_node != nullptr) {
|
||||
// If so, get this node's sockets and loop through them
|
||||
QVector<QRectF> mouse_nodes_sockets = mouse_node->GetNodeSocketRects();
|
||||
for (int i=0;i<mouse_nodes_sockets.size();i++) {
|
||||
|
||||
// See if this socket contains the mouse cursor
|
||||
if (mouse_nodes_sockets.at(i).contains(event->scenePos() - mouse_node->pos())) {
|
||||
|
||||
// If so, see if the two rows can be connected
|
||||
|
||||
EffectRow* local_row = central_widget_->GetEffect()->row(clicked_socket_);
|
||||
EffectRow* remote_row = mouse_node->GetRowFromIndex(i);
|
||||
|
||||
// Ensure one is an input and one is an output and whether their data types are compatible
|
||||
if (local_row->IsNodeInput() != remote_row->IsNodeInput()) {
|
||||
|
||||
// Determine which row is the input and which row is the output
|
||||
EffectRow* input_row = (local_row->IsNodeInput()) ? local_row : remote_row;
|
||||
EffectRow* output_row = (local_row->IsNodeInput()) ? remote_row : local_row;
|
||||
|
||||
if (input_row->CanAcceptDataType(output_row->OutputDataType())) {
|
||||
// This is a valid connection
|
||||
line_is_touching_node = true;
|
||||
|
||||
// Snap drag line to this socket
|
||||
drag_line_end = mouse_node->pos() + mouse_nodes_sockets.at(i).center();
|
||||
|
||||
// Cancel loop since we have a connection now
|
||||
j = mouse_items.size();
|
||||
|
||||
// Cache destination row
|
||||
drag_destination_ = remote_row;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (line_is_touching_node) {
|
||||
drag_line_->setPen(QPen(Qt::white, 2));
|
||||
} else {
|
||||
drag_line_->setPen(QPen(Qt::gray, 2));
|
||||
}
|
||||
|
||||
drag_line_->setPath(NodeEdgeUI::GetEdgePath(drag_line_start_, drag_line_end));
|
||||
|
||||
event->accept();
|
||||
|
||||
@@ -132,26 +196,16 @@ void NodeUI::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
if (clicked_socket_ > -1) {
|
||||
scene()->removeItem(drag_line_);
|
||||
delete drag_line_;
|
||||
event->accept();
|
||||
|
||||
if (drag_destination_ != nullptr) {
|
||||
EffectRow::ConnectEdge(central_widget_->GetEffect()->row(clicked_socket_), drag_destination_);
|
||||
}
|
||||
} else {
|
||||
QGraphicsItem::mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
QPainterPath NodeUI::GetEdgePath(const QPointF &start_pos, const QPointF &end_pos)
|
||||
{
|
||||
double mid_x = double_lerp(start_pos.x(), end_pos.x(), 0.5);
|
||||
|
||||
QPainterPath path_;
|
||||
path_.moveTo(start_pos);
|
||||
path_.cubicTo(QPointF(mid_x, start_pos.y()),
|
||||
QPointF(mid_x, end_pos.y()),
|
||||
end_pos);
|
||||
|
||||
return path_;
|
||||
}
|
||||
|
||||
QVector<QRectF> NodeUI::GetNodeSocketRects()
|
||||
{
|
||||
QVector<QRectF> rects;
|
||||
@@ -176,3 +230,14 @@ QVector<QRectF> NodeUI::GetNodeSocketRects()
|
||||
|
||||
return rects;
|
||||
}
|
||||
|
||||
EffectRow *NodeUI::GetRowFromIndex(int i)
|
||||
{
|
||||
if (central_widget_ != nullptr) {
|
||||
Node* e = central_widget_->GetEffect();
|
||||
if (i < e->row_count()) {
|
||||
return e->row(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+13
-3
@@ -5,6 +5,8 @@
|
||||
#include <QGraphicsItem>
|
||||
|
||||
class EffectUI;
|
||||
class EffectRow;
|
||||
class NodeEdge;
|
||||
|
||||
class NodeUI : public QGraphicsRectItem {
|
||||
public:
|
||||
@@ -14,24 +16,32 @@ public:
|
||||
void AddToScene(QGraphicsScene* scene);
|
||||
void Resize(const QSize& s);
|
||||
void SetWidget(EffectUI* widget);
|
||||
EffectUI* Widget();
|
||||
|
||||
QVector<QRectF> GetNodeSocketRects();
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent * event) override;
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
QVector<QRectF> GetNodeSocketRects();
|
||||
QPainterPath GetEdgePath(const QPointF& start_pos, const QPointF& end_pos);
|
||||
EffectRow *GetRowFromIndex(int i);
|
||||
|
||||
EffectUI* central_widget_;
|
||||
QGraphicsProxyWidget* proxy_;
|
||||
QPainterPath path_;
|
||||
|
||||
QGraphicsPathItem* drag_line_;
|
||||
QPointF drag_line_start_;
|
||||
EffectRow* drag_destination_;
|
||||
QPainterPath drag_path_;
|
||||
|
||||
int clicked_socket_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // NODEUI_H
|
||||
|
||||
+1
-1
@@ -42,9 +42,9 @@ void NodeView::mouseMoveEvent(QMouseEvent *event)
|
||||
void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!hand_moving_) {
|
||||
hand_moving_ = false;
|
||||
QGraphicsView::mouseReleaseEvent(event);
|
||||
}
|
||||
hand_moving_ = false;
|
||||
}
|
||||
|
||||
void NodeView::wheelEvent(QWheelEvent *event)
|
||||
|
||||
Reference in New Issue
Block a user