From 3ec133f2540680fd440fb143921391bc4af807a3 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 15 Jul 2019 12:43:56 -0400 Subject: [PATCH] can visually see connections between parameters in the node view --- app/node/node.cpp | 10 ++ app/node/node.h | 11 ++ app/node/param.cpp | 5 + app/node/param.h | 2 + app/widget/nodeview/nodeviewedge.cpp | 35 +++-- app/widget/nodeview/nodeviewedge.h | 3 + app/widget/nodeview/nodeviewitem.cpp | 184 +++++++++++++++++++++++---- app/widget/nodeview/nodeviewitem.h | 19 +++ 8 files changed, 233 insertions(+), 36 deletions(-) diff --git a/app/node/node.cpp b/app/node/node.cpp index 6431edc9a..7fcff31d4 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -60,3 +60,13 @@ QList Node::parameters() { return static_qobjectlist_cast(children()); } + +int Node::ParameterCount() +{ + return children().size(); +} + +int Node::IndexOfParameter(NodeParam *param) +{ + return children().indexOf(param); +} diff --git a/app/node/node.h b/app/node/node.h index f159549b9..0490b3650 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -53,6 +53,17 @@ public: */ QList parameters(); + /** + * @brief Return the current number of parameters + */ + int ParameterCount(); + + /** + * @brief Return the index of a parameter + * @return Parameter index or -1 if this parameter is not part of this Node + */ + int IndexOfParameter(NodeParam* param); + public slots: virtual void Process(const rational& time) = 0; }; diff --git a/app/node/param.cpp b/app/node/param.cpp index 56aa191ca..5f4f866d9 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -45,6 +45,11 @@ Node *NodeParam::parent() return static_cast(QObject::parent()); } +int NodeParam::index() +{ + return parent()->IndexOfParameter(this); +} + const QVector &NodeParam::edges() { return edges_; diff --git a/app/node/param.h b/app/node/param.h index 4f4fb8ac0..a7efa8278 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -62,6 +62,8 @@ public: Node* parent(); + int index(); + const QVector& edges(); static bool AreDataTypesCompatible(const DataType& output_type, const DataType& input_type); diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index 793b77513..87cb9e1f9 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -25,7 +25,6 @@ #include "common/clamp.h" #include "common/lerp.h" #include "nodeview.h" -#include "nodeviewitem.h" NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) : QGraphicsLineItem(parent), @@ -57,16 +56,36 @@ void NodeViewEdge::Adjust() NodeViewItem* output = NodeView::NodeToUIObject(scene(), edge_->output()->parent()); NodeViewItem* input = NodeView::NodeToUIObject(scene(), edge_->input()->parent()); - // Calculate output/input points - qreal value = clamp(0.5 + (output->pos().y() - input->pos().y()) / (input->rect().height()) / 4, 0.0, 1.0); + // Create initial values + QPointF output_point = QPointF(output->pos().x() + output->rect().width(), 0); + QPointF input_point = QPointF(input->pos().x(), 0); - // Use a lerp function to draw the line between the two corners - qreal output_point = output->pos().y() + lerp(0.0, output->rect().height(), 1.0 - value); - qreal input_point = input->pos().y() + lerp(0.0, input->rect().height(), value); + // Calculate output/input points + output_point.setY(CalculateEdgeYPoint(output, edge_->output()->index(), input)); + input_point.setY(CalculateEdgeYPoint(input, edge_->input()->index(), output)); // Draw a line between the two setLine(QLineF( - QPointF(output->pos().x() + output->rect().width(), output_point), - QPointF(input->pos().x(), input_point) + output_point, + input_point )); } + +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; + } +} + diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h index 7aed55c9a..8f78017bc 100644 --- a/app/widget/nodeview/nodeviewedge.h +++ b/app/widget/nodeview/nodeviewedge.h @@ -24,6 +24,7 @@ #include #include "node/edge.h" +#include "nodeviewitem.h" class NodeViewEdge : public QGraphicsLineItem { @@ -35,6 +36,8 @@ public: void Adjust(); private: + qreal CalculateEdgeYPoint(NodeViewItem* item, int param_index, NodeViewItem* opposing); + NodeEdgePtr edge_; }; diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index acc88fc3f..235b795e5 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -21,6 +21,7 @@ #include "nodeviewitem.h" #include +#include #include #include @@ -33,7 +34,8 @@ const int kNodeViewItemIconPadding = 12; NodeViewItem::NodeViewItem(QGraphicsItem *parent) : QGraphicsRectItem(parent), - node_(nullptr) + node_(nullptr), + expanded_(false) { // Set flags for this widget setFlag(QGraphicsItem::ItemIsMovable); @@ -43,7 +45,12 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) : QFont f; QFontMetrics fm(f); - setRect(0, 0, kNodeViewItemWidth, fm.height() + kNodeViewItemTextPadding * 2); + // Set default "collapsed" size + title_bar_rect_ = QRectF(0, 0, kNodeViewItemWidth, fm.height() + kNodeViewItemTextPadding * 2); + setRect(title_bar_rect_); + + // Set default node connector size + node_connector_size_ = fm.height() / 3; // FIXME: Magic "number"/magic "color" - allow this to be editable by the user SetColor(QColor(32, 32, 128)); @@ -63,6 +70,8 @@ void NodeViewItem::SetNode(Node *n) { node_ = n; + param_rect_.resize(node_->ParameterCount()); + update(); } @@ -71,53 +80,172 @@ Node *NodeViewItem::node() return node_; } +bool NodeViewItem::IsExpanded() +{ + return expanded_; +} + +void NodeViewItem::SetExpanded(bool e) +{ + expanded_ = e; + + QRectF new_rect; + + if (expanded_) { + QRectF full_size_rect = title_bar_rect_; + + // If a node is connected, use its parameter count to set the height + if (node_ != nullptr) { + QFont f; + QFontMetrics fm(f); + + full_size_rect.adjust(0, 0, 0, kNodeViewItemTextPadding*2 + fm.height() * node_->ParameterCount()); + } + + // Store content_rect (the rect without the titlebar) + content_rect_ = full_size_rect.adjusted(0, title_bar_rect_.height(), 0, 0); + + new_rect = full_size_rect; + } else { + new_rect = title_bar_rect_; + } + + //update(); + + setRect(new_rect); +} + +const QRectF &NodeViewItem::GetParameterConnectorRect(int index) +{ + return param_rect_.at(index); +} + void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { // Set up border, which will change color if selected - QPen pen; + // FIXME: Color not configurable? + QPen border_pen(Qt::black, kNodeViewItemBorderWidth); - pen.setWidth(kNodeViewItemBorderWidth); + // FIXME: The text is always drawn white assuming the color will be dark - the intention is to provide preset + // colors that will always be dark for the user to choose, so this value can stay white. + QPen text_pen(Qt::white); - if (option->state & QStyle::State_Selected) { - pen.setColor(widget->palette().highlight().color()); - } else { - // FIXME: Not configurable? - pen.setColor(Qt::black); + // FIXME: Same as text_pen + QBrush connector_brush(Qt::white); + + // FIXME: Same as above + QBrush content_brush(QColor("#181818")); + + painter->setPen(border_pen); + + if (expanded_ && node_ != nullptr) { + // Draw background rect + painter->setBrush(content_brush); + painter->drawRect(rect()); + + QRectF text_rect = content_rect_.adjusted(kNodeViewItemTextPadding + node_connector_size_, + kNodeViewItemTextPadding, + -kNodeViewItemTextPadding, + -(kNodeViewItemTextPadding + node_connector_size_)); + + // Set pen to draw text + painter->setPen(text_pen); + + // Draw text and a connector rectangle for each parameter + + // Store the rectangles/points which will steadily increase sa we loop + QRectF input_connector_rect(rect().x(), + text_rect.y() + painter->fontMetrics().height() / 2 - node_connector_size_ / 2, + node_connector_size_, + node_connector_size_); + QRectF output_connector_rect = input_connector_rect.translated(rect().width() - node_connector_size_, 0); + QPointF text_draw_point = text_rect.topLeft() + QPointF(0, painter->fontMetrics().ascent()); + + // Loop through all the parameters + QList node_params = node_->parameters(); + for (int i=0;itype() == NodeParam::kInput || param->type() == NodeParam::kBidirectional) { + painter->fillRect(input_connector_rect, connector_brush); + + // FIXME: I don't know how this will work with NodeParam::kBidirectional + param_rect_[i] = input_connector_rect; + } + if (param->type() == NodeParam::kOutput || param->type() == NodeParam::kBidirectional) { + painter->fillRect(output_connector_rect, connector_brush); + + // FIXME: I don't know how this will work with NodeParam::kBidirectional + param_rect_[i] = output_connector_rect; + } + + input_connector_rect.translate(0, painter->fontMetrics().height()); + output_connector_rect.translate(0, painter->fontMetrics().height()); + + // Draw text + painter->drawText(text_draw_point, node_params.at(i)->name()); + text_draw_point += QPointF(0, painter->fontMetrics().height()); + } + + painter->setPen(border_pen); } // Draw rect - painter->setPen(pen); painter->setBrush(brush()); - painter->drawRect(rect()); + painter->drawRect(title_bar_rect_); + + // If selected, draw selection outline + if (option->state & QStyle::State_Selected) { + QPen pen = painter->pen(); + pen.setColor(widget->palette().highlight().color()); + painter->setPen(pen); + + painter->setBrush(Qt::transparent); + + painter->drawRect(rect()); + } // Draw text if (node_ != nullptr) { - // FIXME: The text is always drawn white assuming the color will be dark - the intention is to provide preset - // colors that will always be dark for the user to choose, so this value can stay white. - painter->setPen(Qt::white); + painter->setPen(text_pen); // Draw the expand icon - QRectF icon_rect = rect(); - icon_rect.adjust(kNodeViewItemIconPadding, - kNodeViewItemIconPadding, - -kNodeViewItemIconPadding, - -kNodeViewItemIconPadding); - olive::icon::TriRight.paint(painter, icon_rect.toRect(), Qt::AlignLeft | Qt::AlignVCenter); + expand_hitbox_ = title_bar_rect_.adjusted(kNodeViewItemIconPadding, + kNodeViewItemIconPadding, + -kNodeViewItemIconPadding, + -kNodeViewItemIconPadding); + + // Make the icon rect a square + expand_hitbox_.setWidth(expand_hitbox_.height()); + + // Draw the icon + olive::icon::TriRight.paint(painter, expand_hitbox_.toRect(), Qt::AlignLeft | Qt::AlignVCenter); // Draw the text in a rect (the rect is sized around text already in the constructor) - QRectF text_rect = rect(); - text_rect.adjust(kNodeViewItemIconPadding + icon_rect.height() + kNodeViewItemTextPadding, - kNodeViewItemTextPadding, - -kNodeViewItemTextPadding, - -kNodeViewItemTextPadding); - painter->drawText(text_rect, Qt::AlignTop | Qt::AlignLeft, node_->Name()); + QRectF text_rect = title_bar_rect_.adjusted(kNodeViewItemIconPadding + expand_hitbox_.width() + kNodeViewItemTextPadding, + kNodeViewItemTextPadding, + -kNodeViewItemTextPadding, + -kNodeViewItemTextPadding); + painter->drawText(text_rect, Qt::AlignVCenter | Qt::AlignLeft, node_->Name()); } } +void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + // Check if we clicked the Expand/Collapse icon + if (expand_hitbox_.contains(event->pos())) { + SetExpanded(!IsExpanded()); + } + + QGraphicsRectItem::mouseReleaseEvent(event); +} + void NodeViewItem::UpdateGradient() { - QLinearGradient grad(QPointF(0, rect().top()), QPointF(0, rect().bottom())); + /*QLinearGradient grad(QPointF(0, rect().top()), QPointF(0, rect().bottom())); grad.setColorAt(0, color_.lighter(175)); grad.setColorAt(1, color_); - setBrush(grad); + setBrush(grad);*/ + setBrush(color_); } diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index eefac74b6..ed2a52cb3 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -36,15 +36,34 @@ public: void SetNode(Node* n); Node* node(); + bool IsExpanded(); + void SetExpanded(bool e); + + const QRectF& GetParameterConnectorRect(int index); + protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + private: void UpdateGradient(); + QRectF expand_hitbox_; + Node* node_; QColor color_; + + QRectF title_bar_rect_; + + QRectF content_rect_; + + QVector param_rect_; + + int node_connector_size_; + + bool expanded_; }; #endif // NODEVIEWITEM_H