From 378c354277a9e7cdec218393856a077dd12d9777 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 3 Apr 2020 08:34:47 +1100 Subject: [PATCH] nodeview: improved nodeview usage Rather than the cumbersome and small "node connectors" (introduced a while ago when nodes had more than one output), this is a more streamlined system of connecting nodes (hold Ctrl and drag to connect) --- app/widget/nodeview/nodeview.cpp | 10 +- app/widget/nodeview/nodeviewedge.cpp | 28 +- app/widget/nodeview/nodeviewitem.cpp | 489 ++++++++++++-------------- app/widget/nodeview/nodeviewitem.h | 53 ++- app/widget/nodeview/nodeviewscene.cpp | 2 +- 5 files changed, 248 insertions(+), 334 deletions(-) diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index 09549cf71..5bd2e506f 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -192,7 +192,7 @@ void NodeView::keyPressEvent(QKeyEvent *event) void NodeView::mousePressEvent(QMouseEvent *event) { if (attached_item_) { - Node* dropping_node = attached_item_->node(); + Node* dropping_node = attached_item_->GetNode(); DetachItemFromCursor(); @@ -247,7 +247,7 @@ void NodeView::mouseMoveEvent(QMouseEvent *event) NodeInput* compatible_input = nullptr; - foreach (NodeParam* drop_node_param, attached_item_->node()->parameters()) { + foreach (NodeParam* drop_node_param, attached_item_->GetNode()->parameters()) { if (drop_node_param->type() == NodeParam::kInput && static_cast(drop_node_param)->data_type() & input_type) { compatible_input = static_cast(drop_node_param); @@ -346,7 +346,7 @@ void NodeView::PlaceNode(NodeViewItem *n, const QPointF &pos) continue; } - qDebug() << "Moving" << node_item->node() << "for" << n->node(); + qDebug() << "Moving" << node_item->GetNode() << "for" << n->GetNode(); QPointF new_pos; @@ -354,8 +354,8 @@ void NodeView::PlaceNode(NodeViewItem *n, const QPointF &pos) qDebug() << "Same pos, need more info"; // Item positions are exact, we'll need more information to determine where this item should go - Node* ours = n->node(); - Node* theirs = node_item->node(); + Node* ours = n->GetNode(); + Node* theirs = node_item->GetNode(); bool moved = false; diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index 30e35a618..1fe554f30 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -58,24 +58,6 @@ NodeEdgePtr NodeViewEdge::edge() return edge_; } -qreal CalculateEdgeYPoint(NodeViewItem *item, NodeParam* param, NodeViewItem *opposing) -{ - if (item->IsExpanded()) { - return item->pos().y() + item->GetParameterConnectorRect(param).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() + opposing->rect().top()) - (item->pos().y() + item->rect().top())) / 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() + item->rect().top() + lerp(0.0, item->rect().height(), input_value); - - // Set Y values according to calculations - return input_y; - } -} - void NodeViewEdge::Adjust() { if (!edge_ || !scene()) { @@ -90,16 +72,8 @@ void NodeViewEdge::Adjust() return; } - // Create initial values - QPointF output_point = QPointF(output->pos().x() + output->rect().left() + output->rect().width(), 0); - QPointF input_point = QPointF(input->pos().x() + output->rect().left(), 0); - - // Calculate output/input points - output_point.setY(CalculateEdgeYPoint(output, edge_->output(), input)); - input_point.setY(CalculateEdgeYPoint(input, edge_->input(), output)); - // Draw a line between the two - SetPoints(output_point, input_point); + SetPoints(output->GetParamPoint(edge_->output()), input->GetParamPoint(edge_->input())); } void NodeViewEdge::SetConnected(bool c) diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index 9a99cee2b..60257d398 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -26,6 +26,7 @@ #include #include +#include "common/flipmodifiers.h" #include "common/qtutils.h" #include "core.h" #include "nodeview.h" @@ -37,11 +38,12 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) : QGraphicsRectItem(parent), node_(nullptr), - font_metrics(font), dragging_edge_(nullptr), - drag_expanded_item_(nullptr), + cached_drop_item_(nullptr), + cached_drop_item_expanded_(false), expanded_(false), standard_click_(false), + highlighted_index_(-1), node_edge_change_command_(nullptr) { // Set flags for this widget @@ -53,24 +55,21 @@ NodeViewItem::NodeViewItem(QGraphicsItem *parent) : // We use font metrics to set all the UI measurements for DPI-awareness // - // Not particularly great way of using text scaling to set the width (DPI-awareness, etc.) - int widget_width = QFontMetricsWidth(font_metrics, "HHHHHHHHHHHHHHHH"); + QFont default_font; + QFontMetrics font_metrics(default_font); // Set border width node_border_width_ = font_metrics.height() / 12; - // Set default node connector size - node_connector_size_ = font_metrics.height() / 3; - // Set text and icon padding - node_text_padding_ = font_metrics.height() / 6; + int node_text_padding = font_metrics.height() / 4; - // FIXME: Revise icon sizing algorithm (share with NodeParamViewItem) - node_icon_padding_ = node_text_padding_ * 3; + // Not particularly great way of using text scaling to set the width (DPI-awareness, etc.) + int widget_width = QFontMetricsWidth(font_metrics, "HHHHHHHHHHHHHH"); // Use the current default font height to size this widget // Set default "collapsed" size - int widget_height = font_metrics.height() + node_text_padding_ * 2; + int widget_height = font_metrics.height() + node_text_padding * 2; title_bar_rect_ = QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height); setRect(title_bar_rect_); @@ -80,17 +79,33 @@ void NodeViewItem::SetNode(Node *n) { node_ = n; - setPos(node_->GetPosition()); + node_inputs_.clear(); + + if (node_) { + node_->Retranslate(); + + foreach (NodeParam* p, node_->parameters()) { + if (p->type() == NodeParam::kInput) { + NodeInput* input = static_cast(p); + + if (input->IsConnectable()) { + node_inputs_.append(input); + } + } + } + + setPos(node_->GetPosition()); + } update(); } -Node *NodeViewItem::node() +Node *NodeViewItem::GetNode() const { return node_; } -bool NodeViewItem::IsExpanded() +bool NodeViewItem::IsExpanded() const { return expanded_; } @@ -103,27 +118,16 @@ 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) { - full_size_rect.adjust(0, 0, 0, node_text_padding_*2 + font_metrics.height() * node_->parameters().size()); - } - - // 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; + if (expanded_ && !node_inputs_.isEmpty()) { + // Create new rect + QRectF new_rect = title_bar_rect_; + new_rect.setHeight(new_rect.height() * node_inputs_.size()); + setRect(new_rect); } else { - new_rect = title_bar_rect_; + setRect(title_bar_rect_); } update(); - - setRect(new_rect); } void NodeViewItem::ToggleExpanded() @@ -131,153 +135,55 @@ void NodeViewItem::ToggleExpanded() SetExpanded(!IsExpanded()); } -QRectF NodeViewItem::GetParameterConnectorRect(int index) -{ - if (node_ == nullptr) { - return QRectF(); - } - - NodeParam* param = node_->parameters().at(index); - - QRectF connector_rect(rect().x(), - content_rect_.y() + node_text_padding_ + font_metrics.height() / 2 - node_connector_size_ / 2, - node_connector_size_, - node_connector_size_); - - if (index > 0) { - connector_rect.translate(0, font_metrics.height() * index); - } - - if (param->type() == NodeParam::kOutput) { - connector_rect.translate(rect().width() - node_connector_size_, 0); - } - - return connector_rect; -} - -QRectF NodeViewItem::GetParameterConnectorRect(NodeParam *param) -{ - NodeParam* root_param = param; - Node* parent_node = param->parentNode(); - - while (root_param->parent() != parent_node) { - root_param = static_cast(root_param->parent()); - } - - return GetParameterConnectorRect(root_param->index()); -} - -QPointF NodeViewItem::GetParameterTextPoint(int index) -{ - if (node_ == nullptr) { - return QPointF(); - } - - NodeParam* param = node_->parameters().at(index); - - if (param->type() == NodeParam::kOutput) { - return content_rect_.topRight() + QPointF(-(node_connector_size_ + node_text_padding_), - node_text_padding_ + font_metrics.ascent() + font_metrics.height()*index); - } else { - return content_rect_.topLeft() + QPointF(node_connector_size_ + node_text_padding_, - node_text_padding_ + font_metrics.ascent() + font_metrics.height()*index); - } -} - void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { // HACK for getting the main QWidget palette color (the `widget`'s palette uses the NodeView color instead which we // don't want here) QPalette app_pal = Core::instance()->main_window()->palette(); - // Set up border, which will change color if selected - QPen border_pen(css_proxy_.BorderColor(), node_border_width_); + { + QPen border_pen; + border_pen.setWidth(node_border_width_); - QPen text_pen(app_pal.color(QPalette::Text)); + QBrush bkg_color; - QBrush connector_brush(app_pal.color(QPalette::Text)); + if (option->state & QStyle::State_Selected) { + border_pen.setColor(app_pal.color(QPalette::Highlight)); + } else { + border_pen.setColor(css_proxy_.BorderColor()); + } - painter->setPen(border_pen); - - if (expanded_ && node_ != nullptr) { - - // Use main widget color for node contents - painter->setBrush(app_pal.window()); - - // Draw background rect - painter->drawRect(rect()); - - // Set pen to draw text - painter->setPen(text_pen); - - // Draw text and a connector rectangle for each parameter - - // Store the text points which will steadily increase sa we loop - - // Loop through all the parameters - for (int i=0;iparameters().size();i++) { - NodeParam* param = node_->parameters().at(i); - - // If parameter is not connectable, don't draw its slot - if (param->IsConnectable()) { - // Draw connector square - painter->fillRect(GetParameterConnectorRect(i), connector_brush); - } - - // Draw text - QPointF text_pt = GetParameterTextPoint(i); - - if (param->type() == NodeParam::kOutput) { - text_pt -= QPointF(QFontMetricsWidth(font_metrics, param->name()), 0); - } - - painter->drawText(text_pt, param->name()); + if (IsExpanded()) { + bkg_color = app_pal.color(QPalette::Window); + } else { + bkg_color = css_proxy_.TitleBarColor(); } painter->setPen(border_pen); - } - - // Draw rect - painter->setBrush(css_proxy_.TitleBarColor()); - painter->drawRect(title_bar_rect_); - - // If selected, draw selection outline - if (option->state & QStyle::State_Selected) { - QPen pen = painter->pen(); - pen.setColor(app_pal.color(QPalette::Highlight)); - painter->setPen(pen); - - painter->setBrush(Qt::transparent); + painter->setBrush(bkg_color); painter->drawRect(rect()); } - // Draw text - if (node_ != nullptr) { - painter->setPen(text_pen); + painter->setPen(app_pal.color(QPalette::Text)); - // Draw the expand icon - expand_hitbox_ = title_bar_rect_.adjusted(node_icon_padding_, - node_icon_padding_, - -node_icon_padding_, - -node_icon_padding_); + if (IsExpanded()) { - // Make the icon rect a square - expand_hitbox_.setWidth(expand_hitbox_.height()); + for (int i=0;ifillRect(input_rect, QColor(255, 255, 255, 64)); + } + + painter->drawText(input_rect, Qt::AlignCenter, node_inputs_.at(i)->name()); } + } else if (node_) { + // Draw the text in a rect (the rect is sized around text already in the constructor) - QRectF text_rect = title_bar_rect_.adjusted(node_icon_padding_ + expand_hitbox_.width() + node_text_padding_, - node_text_padding_, - -node_text_padding_, - -node_text_padding_); - painter->drawText(text_rect, static_cast(Qt::AlignVCenter | Qt::AlignLeft), node_->Name()); + painter->drawText(title_bar_rect_, Qt::AlignCenter, node_->Name()); + } } @@ -288,84 +194,78 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event) // base class behavior is valid here. standard_click_ = false; - // Don't initiate a drag if we clicked the expand hitbox - if (expand_hitbox_.contains(event->pos())) { - return; - } + // If CTRL is held, we start a new edge + if (node_ && event->modifiers() & Qt::ControlModifier) { - // See if the mouse click was on a parameter connector - 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;iparameters().size();i++) { + NodeParam* param = node_->output(); - if (GetParameterConnectorRect(i).contains(event->pos())) { // See if the cursor is in the rect + // Create draggable object + dragging_edge_ = new NodeViewEdge(); - NodeParam* param = node_->parameters().at(i); + // Set up a QUndoCommand to make this action undoable + node_edge_change_command_ = new QUndoCommand(); - // Create draggable object - dragging_edge_ = new NodeViewEdge(); + 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 + drag_source_ = this; + drag_src_param_ = param; - // Set up a QUndoCommand to make this action undoable - node_edge_change_command_ = new QUndoCommand(); + // Set the starting position to the current param's connector + dragging_edge_start_ = GetParamPoint(param); - 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 - drag_source_ = this; - drag_src_param_ = param; + } 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(); - // Set the starting position to the current param's connector - dragging_edge_start_ = mapToScene(GetParameterConnectorRect(i).center()); + // The starting position will be the OPPOSING parameter's rectangle - } 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(); + // Get the opposing parameter + drag_src_param_ = edge->output(); - // The starting position will be the OPPOSING parameter's rectangle + // Get its Node UI object + drag_source_ = static_cast(scene())->NodeToUIObject(drag_src_param_->parentNode()); - // Get the opposing parameter - drag_src_param_ = edge->output(); - - // Get its Node UI object - drag_source_ = static_cast(scene())->NodeToUIObject(drag_src_param_->parentNode()); - - // Get the opposing parameter's rect center using the line's current coordinates - // (we use the current coordinates because a complex formula is used for the line's coords if the opposing - // node is collapsed, therefore it's easier to just retrieve it from line itself) - NodeViewEdge* existing_edge_ui = static_cast(scene())->EdgeToUIObject(edge); - QPainterPath existing_edge_line = existing_edge_ui->path(); - QPointF edge_start = existing_edge_line.pointAtPercent(0); - QPointF edge_end = existing_edge_line.pointAtPercent(1); - if (existing_edge_ui->contains(edge_start)) { - dragging_edge_start_ = edge_start; - } else { - dragging_edge_start_ = edge_end; - } - - - // Remove old edge - NodeEdgeRemoveCommand* remove_command = new NodeEdgeRemoveCommand(edge->output(), - edge->input(), - node_edge_change_command_); - remove_command->redo(); - - } - - // Add it to the scene - scene()->addItem(dragging_edge_); - - // Trigger initial line setting - mouseMoveEvent(event); - - return; + // Get the opposing parameter's rect center using the line's current coordinates + // (we use the current coordinates because a complex formula is used for the line's coords if the opposing + // node is collapsed, therefore it's easier to just retrieve it from line itself) + NodeViewEdge* existing_edge_ui = static_cast(scene())->EdgeToUIObject(edge); + QPainterPath existing_edge_line = existing_edge_ui->path(); + QPointF edge_start = existing_edge_line.pointAtPercent(0); + QPointF edge_end = existing_edge_line.pointAtPercent(1); + if (existing_edge_ui->contains(edge_start)) { + dragging_edge_start_ = edge_start; + } else { + dragging_edge_start_ = edge_end; } - } - } - // We aren't using any override behaviors, switch back to standard click behavior - standard_click_ = true; - QGraphicsRectItem::mousePressEvent(event); + // Remove old edge + NodeEdgeRemoveCommand* remove_command = new NodeEdgeRemoveCommand(edge->output(), + edge->input(), + node_edge_change_command_); + remove_command->redo(); + + } + + // Add it to the scene + scene()->addItem(dragging_edge_); + + // Trigger initial line setting + mouseMoveEvent(event); + + return; + + } else { + + // We aren't using any override behaviors, switch back to standard click behavior + standard_click_ = true; + + event->setModifiers(FlipControlAndShiftModifiers(event->modifiers())); + + QGraphicsRectItem::mousePressEvent(event); + + } } void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) @@ -375,59 +275,67 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) QPointF end_point = event->scenePos(); drag_dest_param_ = nullptr; - // See if the mouse is currently inside a node - NodeViewItem* drop_item = dynamic_cast(scene()->itemAt(event->scenePos(), sceneTransform())); + if (!cached_drop_item_ + || !cached_drop_item_->contains(cached_drop_item_->mapFromScene(event->scenePos()))) { - // If we expanded an item below but are no longer dragging over it, re-collapse it - if (drag_expanded_item_ != nullptr && drop_item != drag_expanded_item_) { - drag_expanded_item_->SetExpanded(false); - drag_expanded_item_ = nullptr; - } + // Cursor has left last entered item, we need to find a new one + if (cached_drop_item_) { - if (drop_item != nullptr && drop_item != drag_source_) { + // If we expanded an item below but are no longer dragging over it, re-collapse it + if (cached_drop_item_expanded_) { + cached_drop_item_->SetExpanded(false); + } - // If the item we're dragging over is collapsed, expand it - if (!drop_item->IsExpanded()) { - drag_expanded_item_ = drop_item; - drop_item->SetExpanded(true); + cached_drop_item_->setZValue(0); + cached_drop_item_ = nullptr; } - // See if the mouse is currently inside a connector rect - for (int i=0;inode()->parameters().size();i++) { + NodeViewItem* cursor_item = dynamic_cast(scene()->itemAt(event->scenePos(), sceneTransform())); - // Make a larger "hitbox" rect to make it easier to drag into - QRectF param_hitbox = drop_item->GetParameterConnectorRect(i).adjusted(-node_connector_size_, - -node_connector_size_, - node_connector_size_, - node_connector_size_); + if (cursor_item + && cursor_item != drag_source_ + && !cursor_item->node_inputs_.isEmpty()) { + cached_drop_item_ = cursor_item; + + // If the item we're dragging over is collapsed, expand it + cached_drop_item_expanded_ = !cached_drop_item_->IsExpanded(); + + if (cached_drop_item_expanded_) { + cached_drop_item_->SetExpanded(true); + } + + cached_drop_item_->setZValue(1); + } else { + cached_drop_item_ = nullptr; + } + + } + + if (cached_drop_item_) { + + int highlight_their_index = -1; + + // See if the mouse is currently inside a connector rect + for (int i=0;inode_inputs_.size();i++) { // Get the parameter we're dragging into - NodeParam* comp_param = drop_item->node()->parameters().at(i); + NodeParam* comp_param = cached_drop_item_->node_inputs_.at(i); - if (comp_param->IsConnectable() && param_hitbox.contains(drop_item->mapFromScene(event->scenePos()))) { // See if we're dragging inside the hitbox + // See if cursor is inside its UI + if (cached_drop_item_->GetInputRect(i).contains(cached_drop_item_->mapFromScene(event->scenePos()))) { // See if we're dragging inside the hitbox - // Prevent circular dependency - check if the Node we'll be outputting to already outputs to this Node - Node* outputting_node; - Node* receiving_node; - - // Determine which Node will be "submitting output" and which node will be "receiving input" - if (drag_src_param_->type() == NodeParam::kInput) { - receiving_node = drag_src_param_->parentNode(); - outputting_node = drop_item->node(); - } else { - receiving_node = drop_item->node(); - outputting_node = drag_src_param_->parentNode(); - } - - // Ensure the receiving node doesn't output to the outputting node - if (!receiving_node->OutputsTo(outputting_node)) { + // Attempt to prevent circular dependency - ensure the receiving node doesn't output to the outputting node + if (!cached_drop_item_->GetNode()->OutputsTo(node_)) { drag_dest_param_ = comp_param; - end_point = drop_item->mapToScene(drop_item->GetParameterConnectorRect(i).center()); + highlight_their_index = i; + end_point = cached_drop_item_->mapToScene(cached_drop_item_->GetInputPoint(i)); } break; } } + + cached_drop_item_->SetHighlightedIndex(highlight_their_index); } dragging_edge_->SetConnected(drag_dest_param_ != nullptr); @@ -438,6 +346,8 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) } if (standard_click_) { + event->setModifiers(FlipControlAndShiftModifiers(event->modifiers())); + QGraphicsRectItem::mouseMoveEvent(event); } } @@ -451,9 +361,9 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) scene()->removeItem(dragging_edge_); // If we expanded an item in the drag, re-collapse it now - if (drag_expanded_item_ != nullptr) { - drag_expanded_item_->SetExpanded(false); - drag_expanded_item_ = nullptr; + if (cached_drop_item_ != nullptr) { + cached_drop_item_->SetExpanded(false); + cached_drop_item_ = nullptr; } if (drag_dest_param_ != nullptr) { @@ -486,21 +396,13 @@ void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) return; } - // Check if we clicked the Expand/Collapse icon - if (expand_hitbox_.contains(event->pos())) { - ToggleExpanded(); - } - if (standard_click_) { + event->setModifiers(FlipControlAndShiftModifiers(event->modifiers())); + QGraphicsRectItem::mouseReleaseEvent(event); } } -void NodeViewItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) -{ - ToggleExpanded(); -} - QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { if (change == ItemPositionHasChanged && node_) { @@ -509,3 +411,48 @@ QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons return QGraphicsItem::itemChange(change, value); } + +void NodeViewItem::SetHighlightedIndex(int index) +{ + if (highlighted_index_ == index) { + return; + } + + highlighted_index_ = index; + + update(); +} + +QRectF NodeViewItem::GetInputRect(int index) const +{ + QRectF r = title_bar_rect_; + + if (IsExpanded()) { + r.translate(0, r.height() * index); + } + + return r; +} + +QPointF NodeViewItem::GetParamPoint(NodeParam *param) const +{ + if (param->type() == NodeParam::kOutput) { + return pos() + QPointF(rect().right(), rect().center().y()); + } else { + NodeInput* input = static_cast(param); + + // Resolve NodeInputArray elements + while (input->parentNode() != input->parent()) { + input = static_cast(input->parent()); + } + + return pos() + GetInputPoint(node_inputs_.indexOf(input)); + } +} + +QPointF NodeViewItem::GetInputPoint(int index) const +{ + QRectF input_rect = GetInputRect(index); + + return QPointF(input_rect.left(), input_rect.center().y()); +} diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index cc03e02d7..020942688 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -51,12 +51,12 @@ public: /** * @brief Get currently attached node */ - Node* node(); + Node* GetNode() const; /** * @brief Get expanded state */ - bool IsExpanded(); + bool IsExpanded() const; /** * @brief Set expanded state @@ -65,17 +65,9 @@ public: void ToggleExpanded(); /** - * @brief Get the rectangle of a specific parameter connector - * - * Useful for drawing parameter connectors (white squares where the Node edges attach) or determining whether a click - * or drag occurred within one. - * - * @param index - * - * Index of the parameter of this node (see NodeParam::index()). + * @brief Returns GLOBAL point that edges should connect to for any NodeParam member of this object */ - QRectF GetParameterConnectorRect(int index); - QRectF GetParameterConnectorRect(NodeParam* index); + QPointF GetParamPoint(NodeParam* param) const; protected: virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; @@ -83,26 +75,35 @@ protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override; virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; - virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override; private: /** - * @brief Get the relative position to draw text for a parameter at a certain index + * @brief Highlight an input (for instance when the user is dragging over it) */ - QPointF GetParameterTextPoint(int index); + void SetHighlightedIndex(int index); /** - * @brief Variable for the expand/collapse button rect (stored for clicking) + * @brief Returns local rect of a NodeInput in array node_inputs_[index] */ - QRectF expand_hitbox_; + QRectF GetInputRect(int index) const; + + /** + * @brief Returns local point that edges should connect to for a NodeInput in array node_inputs_[index] + */ + QPointF GetInputPoint(int index) const; /** * @brief Reference to attached Node */ Node* node_; + /** + * @brief Cached list of node inputs + */ + QList node_inputs_; + /** * @brief A QWidget that can receive CSS properties that NodeViewItem can use * @@ -115,27 +116,17 @@ private: */ QRectF title_bar_rect_; - /** - * @brief Rectangle of the Node's content (zero-size when collapsed, (rect() - title_bar_rect_) when expanded) - */ - QRectF content_rect_; - - /// Used to determine certain padding/margin variables for high DPI support - QFont font; - QFontMetrics font_metrics; - /// Edge dragging variables NodeViewEdge* dragging_edge_; QPointF dragging_edge_start_; NodeParam* drag_src_param_; NodeParam* drag_dest_param_; NodeViewItem* drag_source_; - NodeViewItem* drag_expanded_item_; + + NodeViewItem* cached_drop_item_; + bool cached_drop_item_expanded_; /// Sizing variables to use when drawing - int node_connector_size_; - int node_text_padding_; - int node_icon_padding_; int node_border_width_; /** @@ -150,6 +141,8 @@ private: */ bool standard_click_; + int highlighted_index_; + /** * @brief QUndoCommand for creating and deleting edges by dragging * diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp index dffd8702f..0e44fe550 100644 --- a/app/widget/nodeview/nodeviewscene.cpp +++ b/app/widget/nodeview/nodeviewscene.cpp @@ -213,7 +213,7 @@ void NodeViewScene::ReorganizeInternal(NodeViewItem* src_item, QHash return; } - Node* n = src_item->node(); + Node* n = src_item->GetNode(); QList connected_nodes = n->GetImmediateDependencies();