diff --git a/app/node/input.cpp b/app/node/input.cpp index 34587e5f2..eb4390394 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -834,4 +834,9 @@ QVariant NodeInput::StringToValue(const QString &string, QListget_closest_keyframe_after_time(time); } + struct KeyframeTrackReference { + NodeInput* input; + int element; + int track; + + bool operator==(const KeyframeTrackReference& rhs) const + { + return input == rhs.input && element == rhs.element && track == rhs.track; + } + }; + signals: void NameChanged(const QString& name); @@ -471,6 +482,8 @@ private slots: }; +uint qHash(const NodeInput::KeyframeTrackReference& ref, uint seed = 0); + } #endif // NODEINPUT_H diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index 2370c6459..b07609e51 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -61,49 +61,45 @@ void CurveView::Clear() lines_.clear(); } -void CurveView::ConnectInput(NodeInput *input) +void CurveView::ConnectInput(NodeInput *input, int element, int track) { - if (connected_inputs_.contains(input)) { + NodeInput::KeyframeTrackReference ref = {input, element, track}; + + if (connected_inputs_.contains(ref)) { // Input wasn't connected, do nothing return; } - // Add keyframes from each subelement including primary (-1) - for (int i=-1; iArraySize(); i++) { - ConnectInputElement(input, i); + // Add keyframes from track + foreach (NodeKeyframe* key, input->GetKeyframeTracks(element).at(track)) { + this->AddKeyframe(key); + } + + if (!keyframe_colors_.contains(ref)) { + // Generate a random color for this input + keyframe_colors_.insert(ref, QColor::fromHsv(std::rand()%360, std::rand()%255, 255)); } // Append to the list - connected_inputs_.append(input); - - // Connect add/remove signals - connect(input, &NodeInput::KeyframeAdded, this, &CurveView::AddKeyframe); - connect(input, &NodeInput::KeyframeRemoved, this, &CurveView::RemoveKeyframe); + connected_inputs_.append(ref); } -void CurveView::DisconnectNode(Node *node) +void CurveView::DisconnectInput(NodeInput *input, int element, int track) { - foreach (NodeInput* i, node->parameters()) { - DisconnectInput(i); - } -} + NodeInput::KeyframeTrackReference ref = {input, element, track}; -void CurveView::DisconnectInput(NodeInput *input) -{ - if (!connected_inputs_.contains(input)) { + if (!connected_inputs_.contains(ref)) { // Input wasn't connected, do nothing return; } - // Remove keyframes belonging to this input - RemoveKeyframesOfInput(input); + // Remove keyframes belonging to this element and track + foreach (NodeKeyframe* key, input->GetKeyframeTracks(element).at(track)) { + RemoveKeyframe(key); + } // Remove from the list - connected_inputs_.removeOne(input); - - // Disconnect add/remove signals - disconnect(input, &NodeInput::KeyframeAdded, this, &CurveView::AddKeyframe); - disconnect(input, &NodeInput::KeyframeRemoved, this, &CurveView::RemoveKeyframe); + connected_inputs_.removeOne(ref); } void CurveView::drawBackground(QPainter *painter, const QRectF &rect) @@ -157,91 +153,89 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) painter->drawLines(lines); // Draw keyframe lines - foreach (NodeInput* input, connected_inputs_) { - for (int j=-1; jArraySize(); j++) { - if (input->IsKeyframing(j)) { - const QVector& tracks = input->GetKeyframeTracks(j); + foreach (const NodeInput::KeyframeTrackReference& ref, connected_inputs_) { + NodeInput* input = ref.input; - for (int k=0; kIsKeyframing(ref.element)) { + const QVector& tracks = input->GetKeyframeTracks(ref.element); - if (!track.isEmpty()) { - painter->setPen(QPen(keyframe_colors_.value(GetKeyframeTrackUniqueID(input, j, k)), - qMax(1, fontMetrics().height() / 4))); + const NodeKeyframeTrack& track = tracks.at(ref.track); - QVector keyframe_lines; + if (!track.isEmpty()) { + painter->setPen(QPen(keyframe_colors_.value(ref), + qMax(1, fontMetrics().height() / 4))); - // Draw straight line leading to first keyframe - QPointF first_key_pos = item_map().value(track.first())->pos(); - keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos)); + QVector keyframe_lines; - // Draw lines between each keyframe - for (int i=1;ipos(); + keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos)); - KeyframeViewItem* before_item = item_map().value(before); - KeyframeViewItem* after_item = item_map().value(after); + // Draw lines between each keyframe + for (int i=1;itype() == NodeKeyframe::kHold) { - // Draw a hold keyframe (basically a right angle) - keyframe_lines.append(QLineF(before_item->pos().x(), - before_item->pos().y(), - after_item->pos().x(), - before_item->pos().y())); - keyframe_lines.append(QLineF(after_item->pos().x(), - before_item->pos().y(), - after_item->pos().x(), - after_item->pos().y())); - } else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { - // Draw a cubic bezier + KeyframeViewItem* before_item = item_map().value(before); + KeyframeViewItem* after_item = item_map().value(after); - // Cubic beziers have two control points, so we can just use both - QPointF before_control_point = before_item->pos() + ScalePoint(before->bezier_control_out()); - QPointF after_control_point = after_item->pos() + ScalePoint(after->bezier_control_in()); + if (before->type() == NodeKeyframe::kHold) { + // Draw a hold keyframe (basically a right angle) + keyframe_lines.append(QLineF(before_item->pos().x(), + before_item->pos().y(), + after_item->pos().x(), + before_item->pos().y())); + keyframe_lines.append(QLineF(after_item->pos().x(), + before_item->pos().y(), + after_item->pos().x(), + after_item->pos().y())); + } else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { + // Draw a cubic bezier - QPainterPath path; - path.moveTo(before_item->pos()); - path.cubicTo(before_control_point, after_control_point, after_item->pos()); - painter->drawPath(path); + // Cubic beziers have two control points, so we can just use both + QPointF before_control_point = before_item->pos() + ScalePoint(before->bezier_control_out()); + QPointF after_control_point = after_item->pos() + ScalePoint(after->bezier_control_in()); - } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { - // Draw a quadratic bezier + QPainterPath path; + path.moveTo(before_item->pos()); + path.cubicTo(before_control_point, after_control_point, after_item->pos()); + painter->drawPath(path); - // Quadratic beziers have a single control point, we just have to determine which it is - QPointF key_anchor; - QPointF control_point; + } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { + // Draw a quadratic bezier - if (before->type() == NodeKeyframe::kBezier) { - key_anchor = before_item->pos(); - control_point = before->bezier_control_out(); - } else { - key_anchor = after_item->pos(); - control_point = after->bezier_control_in(); - } + // Quadratic beziers have a single control point, we just have to determine which it is + QPointF key_anchor; + QPointF control_point; - // Scale control point - control_point = key_anchor + ScalePoint(control_point); - - // Create the path from both keyframes - QPainterPath path; - path.moveTo(before_item->pos()); - path.quadTo(control_point, after_item->pos()); - painter->drawPath(path); - - } else { - // Linear to linear - keyframe_lines.append(QLineF(before_item->pos(), after_item->pos())); - } + if (before->type() == NodeKeyframe::kBezier) { + key_anchor = before_item->pos(); + control_point = before->bezier_control_out(); + } else { + key_anchor = after_item->pos(); + control_point = after->bezier_control_in(); } - // Draw straight line leading from end keyframe - QPointF last_key_pos = item_map().value(track.last())->pos(); - keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y()))); + // Scale control point + control_point = key_anchor + ScalePoint(control_point); - painter->drawLines(keyframe_lines); + // Create the path from both keyframes + QPainterPath path; + path.moveTo(before_item->pos()); + path.quadTo(control_point, after_item->pos()); + painter->drawPath(path); + + } else { + // Linear to linear + keyframe_lines.append(QLineF(before_item->pos(), after_item->pos())); } } + + // Draw straight line leading from end keyframe + QPointF last_key_pos = item_map().value(track.last())->pos(); + keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y()))); + + painter->drawLines(keyframe_lines); } } } @@ -305,26 +299,6 @@ void CurveView::ContextMenuEvent(Menu &m) //QAction* reset_zoom_action = m.addAction(tr("Reset Zoom")); } -void CurveView::ConnectInputElement(NodeInput *input, int element) -{ - const QVector& tracks = input->GetKeyframeTracks(element); - - for (int i=0; iAddKeyframe(key); - } - - uint h = GetKeyframeTrackUniqueID(input, element, i); - - if (!keyframe_colors_.contains(h)) { - // Generate a random color for this input - keyframe_colors_.insert(h, QColor::fromHsv(std::rand()%360, std::rand()%255, 255)); - } - } -} - qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key) { return GetItemYFromKeyframeValue(key->value().toDouble()); @@ -359,11 +333,6 @@ void CurveView::CreateBezierControlPoints(KeyframeViewItem* item) connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection); } -uint CurveView::GetKeyframeTrackUniqueID(NodeInput *input, int element, int track) -{ - return ::qHash(input) ^ ::qHash(element) ^ ::qHash(track); -} - void CurveView::KeyframeValueChanged() { NodeKeyframe* key = static_cast(sender()); @@ -449,7 +418,7 @@ void CurveView::AddKeyframe(NodeKeyframe* key) { KeyframeViewItem* item = AddKeyframeInternal(key); SetItemYFromKeyframeValue(key, item); - item->SetOverrideBrush(keyframe_colors_.value(GetKeyframeTrackUniqueID(key->parent(), key->element(), key->track()))); + item->SetOverrideBrush(keyframe_colors_.value({key->parent(), key->element(), key->track()})); connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged); connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged); diff --git a/app/widget/curvewidget/curveview.h b/app/widget/curvewidget/curveview.h index afa0b13bb..72c0e01e4 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -38,11 +38,9 @@ public: virtual void Clear() override; - void ConnectInput(NodeInput* input); + void ConnectInput(NodeInput* input, int element, int track); - void DisconnectNode(Node* node); - - void DisconnectInput(NodeInput* input); + void DisconnectInput(NodeInput* input, int element, int track); public slots: void AddKeyframe(NodeKeyframe* key); @@ -63,8 +61,6 @@ protected: virtual void ContextMenuEvent(Menu &m) override; private: - void ConnectInputElement(NodeInput* input, int element); - qreal GetItemYFromKeyframeValue(NodeKeyframe* key); qreal GetItemYFromKeyframeValue(double value); @@ -76,19 +72,17 @@ private: void CreateBezierControlPoints(KeyframeViewItem *item); - uint GetKeyframeTrackUniqueID(NodeInput* input, int element, int track); - - QMap keyframe_colors_; + QHash keyframe_colors_; int text_padding_; int minimum_grid_space_; - QList lines_; + QVector lines_; - QList bezier_control_points_; + QVector bezier_control_points_; - QList connected_inputs_; + QVector connected_inputs_; private slots: void KeyframeValueChanged(); diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 4f07d330b..847b57001 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -44,8 +44,10 @@ CurveWidget::CurveWidget(QWidget *parent) : tree_view_ = new NodeTreeView(); tree_view_->SetOnlyShowKeyframable(true); + tree_view_->SetShowKeyframeTracksAsRows(true); connect(tree_view_, &NodeTreeView::NodeEnableChanged, this, &CurveWidget::NodeEnabledChanged); connect(tree_view_, &NodeTreeView::InputEnableChanged, this, &CurveWidget::InputEnabledChanged); + connect(tree_view_, &NodeTreeView::InputSelectionChanged, this, &CurveWidget::InputSelectionChanged); splitter->addWidget(tree_view_); QWidget* workarea = new QWidget(); @@ -137,14 +139,14 @@ void CurveWidget::SetNodes(const QVector &nodes) // Detect removed nodes foreach (Node* n, nodes_) { if (!nodes.contains(n)) { - view_->DisconnectNode(n); + ConnectNode(n, false); } } // Detect added nodes foreach (Node* n, nodes) { if (tree_view_->IsNodeEnabled(n) && !nodes_.contains(n)) { - ConnectNode(n); + ConnectNode(n, true); } } @@ -213,18 +215,48 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp) key_control_->SetTime(time); } -void CurveWidget::ConnectNode(Node *n) +void CurveWidget::ConnectNode(Node *node, bool connect) { - foreach (NodeInput* i, n->parameters()) { - if (tree_view_->IsInputEnabled(i)) { - view_->ConnectInput(i); - } + foreach (NodeInput* input, node->parameters()) { + ConnectInput(input, connect); } } -void CurveWidget::DisconnectNode(Node *n) +void CurveWidget::ConnectInput(NodeInput *input, bool connect) { - view_->DisconnectNode(n); + int track_count = NodeValue::get_number_of_keyframe_tracks(input->GetDataType()); + bool multiple_tracks = track_count > 1; + + for (int i=-1; iArraySize(); i++) { + if (tree_view_->IsInputEnabled(input, i, multiple_tracks ? -1 : 0)) { + if (multiple_tracks) { + for (int j=0; jIsInputEnabled(input, i, j)) { + if (connect) { + view_->ConnectInput(input, i, j); + } else { + view_->DisconnectInput(input, i, j); + } + } + } + } else { + if (connect) { + view_->ConnectInput(input, i, 0); + } else { + view_->DisconnectInput(input, i, 0); + } + } + } + } + + // Connect add/remove signals + if (connect) { + QObject::connect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe); + QObject::connect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); + } else { + QObject::disconnect(input, &NodeInput::KeyframeAdded, this, &CurveWidget::AddKeyframe); + QObject::disconnect(input, &NodeInput::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); + } } void CurveWidget::SelectionChanged() @@ -303,20 +335,33 @@ void CurveWidget::KeyControlRequestedTimeChanged(const rational &time) void CurveWidget::NodeEnabledChanged(Node* n, bool e) { - if (e) { - ConnectNode(n); - } else { - DisconnectNode(n); - } + ConnectNode(n, e); } -void CurveWidget::InputEnabledChanged(NodeInput *i, bool e) +void CurveWidget::InputEnabledChanged(NodeInput *i, int element, int track, bool e) { if (e) { - view_->ConnectInput(i); + view_->ConnectInput(i, element, track); } else { - view_->DisconnectInput(i); + view_->DisconnectInput(i, element, track); } } +void CurveWidget::AddKeyframe(NodeKeyframe *key) +{ + view_->AddKeyframe(key); +} + +void CurveWidget::RemoveKeyframe(NodeKeyframe *key) +{ + view_->RemoveKeyframe(key); +} + +void CurveWidget::InputSelectionChanged(NodeInput *input, int element, int track) +{ + Q_UNUSED(track) + + key_control_->SetInput(input, element); +} + } diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index da930b09c..0011546bc 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -69,9 +69,9 @@ private: void UpdateBridgeTime(const int64_t& timestamp); - void ConnectNode(Node* n); + void ConnectNode(Node* node, bool connect); - void DisconnectNode(Node* n); + void ConnectInput(NodeInput* input, bool connect); NodeTreeView* tree_view_; @@ -96,7 +96,13 @@ private slots: void NodeEnabledChanged(Node* n, bool e); - void InputEnabledChanged(NodeInput* i, bool e); + void InputEnabledChanged(NodeInput* i, int element, int track, bool e); + + void AddKeyframe(NodeKeyframe* key); + + void RemoveKeyframe(NodeKeyframe* key); + + void InputSelectionChanged(NodeInput* input, int element, int track); }; diff --git a/app/widget/nodetreeview/nodetreeview.cpp b/app/widget/nodetreeview/nodetreeview.cpp index e1410c369..41a6a3c87 100644 --- a/app/widget/nodetreeview/nodetreeview.cpp +++ b/app/widget/nodetreeview/nodetreeview.cpp @@ -4,9 +4,11 @@ namespace olive { NodeTreeView::NodeTreeView(QWidget *parent) : QTreeWidget(parent), - only_show_keyframable_(false) + only_show_keyframable_(false), + show_keyframe_tracks_as_rows_(false) { connect(this, &NodeTreeView::itemChanged, this, &NodeTreeView::ItemCheckStateChanged); + connect(this, &NodeTreeView::itemSelectionChanged, this, &NodeTreeView::SelectionChanged); Retranslate(); } @@ -16,9 +18,9 @@ bool NodeTreeView::IsNodeEnabled(Node *n) const return !disabled_nodes_.contains(n); } -bool NodeTreeView::IsInputEnabled(NodeInput *i) const +bool NodeTreeView::IsInputEnabled(NodeInput *i, int element, int track) const { - return !disabled_inputs_.contains(i); + return !disabled_inputs_.contains({i, element, track}); } void NodeTreeView::SetNodes(const QVector &nodes) @@ -34,16 +36,32 @@ void NodeTreeView::SetNodes(const QVector &nodes) node_item->setData(0, kItemType, kItemTypeNode); node_item->setData(0, kItemPointer, reinterpret_cast(n)); - foreach (NodeInput* i, n->inputs()) { - if (only_show_keyframable_ && !i->IsKeyframable()) { + foreach (NodeInput* input, n->inputs()) { + if (only_show_keyframable_ && !input->IsKeyframable()) { continue; } - QTreeWidgetItem* input_item = new QTreeWidgetItem(node_item); - input_item->setText(0, i->name()); - input_item->setCheckState(0, disabled_inputs_.contains(i) ? Qt::Unchecked : Qt::Checked); - input_item->setData(0, kItemType, kItemTypeInput); - input_item->setData(0, kItemPointer, reinterpret_cast(i)); + int type_track_count = NodeValue::get_number_of_keyframe_tracks(input->GetDataType()); + bool type_has_multiple_tracks = (type_track_count > 1); + + QTreeWidgetItem* input_item = CreateItem(node_item, input, -1, type_has_multiple_tracks ? -1 : 0); + + if (input->IsArray()) { + for (int i=0; iArraySize(); i++) { + QTreeWidgetItem* element_item = CreateItem(input_item, input, i, type_has_multiple_tracks ? -1 : 0); + + if (type_has_multiple_tracks && show_keyframe_tracks_as_rows_) { + for (int j=0; jGetDataType()) == 1) { + item_name = input->name(); + } else { + switch (track) { + case 0: + item_name = tr("X"); + break; + case 1: + item_name = tr("Y"); + break; + case 2: + item_name = tr("Z"); + break; + case 3: + item_name = tr("W"); + break; + default: + item_name = QString::number(track); + } + } + input_item->setText(0, item_name); + + input_item->setCheckState(0, disabled_inputs_.contains({input, element, track}) ? Qt::Unchecked : Qt::Checked); + input_item->setData(0, kItemType, kItemTypeInput); + input_item->setData(0, kItemPointer, reinterpret_cast(input)); + input_item->setData(0, kItemElement, element); + input_item->setData(0, kItemTrack, track); + + return input_item; +} + void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) { Q_UNUSED(column) @@ -91,20 +145,44 @@ void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) } case kItemTypeInput: { - NodeInput* i = reinterpret_cast(item->data(0, kItemPointer).value()); + NodeInput* input = reinterpret_cast(item->data(0, kItemPointer).value()); + int element = item->data(0, kItemElement).toInt(); + int track = item->data(0, kItemTrack).toInt(); + NodeInput::KeyframeTrackReference i = {input, element, track}; if (item->checkState(0) == Qt::Checked) { if (disabled_inputs_.contains(i)) { disabled_inputs_.removeOne(i); - emit InputEnableChanged(i, true); + emit InputEnableChanged(input, element, track, true); } } else if (!disabled_inputs_.contains(i)) { disabled_inputs_.append(i); - emit InputEnableChanged(i, false); + emit InputEnableChanged(input, element, track, false); } break; } } } +void NodeTreeView::SelectionChanged() +{ + QList sel = selectedItems(); + + NodeInput* selected_input = nullptr; + int selected_element = -1; + int selected_track = -1; + + if (!sel.isEmpty()) { + QTreeWidgetItem* item = sel.first(); + + if (item->data(0, kItemType).toInt() == kItemTypeInput) { + selected_input = reinterpret_cast(item->data(0, kItemPointer).value()); + selected_element = item->data(0, kItemElement).toInt(); + selected_track = item->data(0, kItemTrack).toInt(); + } + } + + emit InputSelectionChanged(selected_input, selected_element, selected_track); +} + } diff --git a/app/widget/nodetreeview/nodetreeview.h b/app/widget/nodetreeview/nodetreeview.h index 0984bcde4..9da7979fa 100644 --- a/app/widget/nodetreeview/nodetreeview.h +++ b/app/widget/nodetreeview/nodetreeview.h @@ -15,20 +15,27 @@ public: bool IsNodeEnabled(Node* n) const; - bool IsInputEnabled(NodeInput* i) const; + bool IsInputEnabled(NodeInput* i, int element, int track) const; void SetOnlyShowKeyframable(bool e) { only_show_keyframable_ = e; } + void SetShowKeyframeTracksAsRows(bool e) + { + show_keyframe_tracks_as_rows_ = e; + } + public slots: void SetNodes(const QVector &nodes); signals: void NodeEnableChanged(Node* n, bool e); - void InputEnableChanged(NodeInput* i, bool e); + void InputEnableChanged(NodeInput* i, int element, int track, bool e); + + void InputSelectionChanged(NodeInput* input, int element, int track); protected: virtual void changeEvent(QEvent* e) override; @@ -36,6 +43,8 @@ protected: private: void Retranslate(); + QTreeWidgetItem *CreateItem(QTreeWidgetItem* parent, NodeInput* input, int element, int track); + enum ItemType { kItemTypeNode, kItemTypeInput @@ -43,18 +52,24 @@ private: static const int kItemType = Qt::UserRole; static const int kItemPointer = Qt::UserRole + 1; + static const int kItemElement = Qt::UserRole + 2; + static const int kItemTrack = Qt::UserRole + 3; QVector nodes_; QVector disabled_nodes_; - QVector disabled_inputs_; + QVector disabled_inputs_; bool only_show_keyframable_; + bool show_keyframe_tracks_as_rows_; + private slots: void ItemCheckStateChanged(QTreeWidgetItem* item, int column); + void SelectionChanged(); + }; }