From 619de01db4ef62247e1c052220bb6269b23c7536 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 17 Dec 2021 17:07:59 -0800 Subject: [PATCH] refined curve view --- app/widget/curvewidget/curveview.cpp | 218 +++++++++++------- app/widget/curvewidget/curveview.h | 14 +- app/widget/curvewidget/curvewidget.cpp | 79 +++---- app/widget/curvewidget/curvewidget.h | 8 +- app/widget/keyframeview/keyframeview.cpp | 10 + app/widget/keyframeview/keyframeview.h | 2 + .../keyframeviewinputconnection.cpp | 8 + .../keyframeviewinputconnection.h | 4 + app/widget/nodeparamview/nodeparamview.cpp | 7 +- .../nodeparamviewwidgetbridge.cpp | 42 +--- app/widget/nodetreeview/nodetreeview.cpp | 43 ++-- app/widget/nodetreeview/nodetreeview.h | 9 + app/widget/slider/floatslider.cpp | 88 ++++--- app/widget/slider/floatslider.h | 4 + app/widget/timebased/timebasedview.h | 11 +- .../timebased/timebasedviewselectionmanager.h | 15 +- 16 files changed, 309 insertions(+), 253 deletions(-) diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index 8fee9d725..e756d7376 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -30,7 +30,6 @@ #include "common/qtutils.h" #include "widget/keyframeview/keyframeviewundo.h" #include "widget/nodeparamview/nodeparamviewundo.h" -#include "widget/slider/floatslider.h" namespace olive { @@ -61,6 +60,9 @@ void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref) track_con->SetBrush(keyframe_colors_.value(ref)); track_connections_.insert(ref, track_con); + // Signal to CurveWidget to update its bezier/linear/hold buttons if a key type changes + connect(track_con, &KeyframeViewInputConnection::TypeChanged, this, &CurveView::SelectionChanged); + // Append to the list connected_inputs_.append(ref); } @@ -90,13 +92,6 @@ void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref) } } -void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref) -{ - if (KeyframeViewInputConnection *con = track_connections_.value(ref)) { - ZoomToFitInternal(con->GetKeyframes()); - } -} - void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color) { // Insert color into hashmap @@ -265,8 +260,28 @@ void CurveView::ContextMenuEvent(Menu &m) void CurveView::SceneRectUpdateEvent(QRectF &r) { - r.setTop(r.top() - this->height()); - r.setBottom(r.bottom() + this->height()); + double min_val, max_val; + bool got_val = false; + + foreach (KeyframeViewInputConnection *con, track_connections_) { + foreach (NodeKeyframe *key, con->GetKeyframes()) { + qreal key_y = GetItemYFromKeyframeValue(key); + + if (got_val) { + min_val = qMin(key_y, min_val); + max_val = qMax(key_y, max_val); + } else { + min_val = key_y; + max_val = key_y; + got_val = true; + } + } + } + + if (got_val) { + r.setTop(min_val - this->height()); + r.setBottom(max_val + this->height()); + } } qreal CurveView::GetKeyframeSceneY(KeyframeViewInputConnection *track, NodeKeyframe *key) @@ -392,7 +407,8 @@ void CurveView::KeyframeDragStart(QMouseEvent *event) { drag_keyframe_values_.resize(GetSelectedKeyframes().size()); for (int i=0; ivalue(); + NodeKeyframe *key = GetSelectedKeyframes().at(i); + drag_keyframe_values_[i] = key->value(); } drag_start_ = mapToScene(event->pos()); @@ -401,7 +417,11 @@ void CurveView::KeyframeDragStart(QMouseEvent *event) void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) { if (event->modifiers() & Qt::ShiftModifier) { - // Lock to X axis only + // Lock to X axis only and set original values on all keys + for (int i=0; iset_value(drag_keyframe_values_.at(i)); + } return; } @@ -412,10 +432,11 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) for (int i=0; iparent(); - double original_val = drag_keyframe_values_.at(i).toDouble(); + double original_val = FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).toDouble(), display); const QString& input = key->input(); - double new_val = original_val - scaled_diff; + double new_val = FloatSlider::TransformDisplayToValue(original_val - scaled_diff, display); double limited = new_val; if (node->HasInputProperty(input, QStringLiteral("min"))) { @@ -434,23 +455,18 @@ void CurveView::KeyframeDragMove(QMouseEvent *event, QString &tip) // Set values for (int i=0; iset_value(drag_keyframe_values_.at(i).toDouble() - scaled_diff); + FloatSlider::DisplayType display = GetFloatDisplayTypeFromKeyframe(key); + key->set_value(FloatSlider::TransformDisplayToValue(FloatSlider::TransformValueToDisplay(drag_keyframe_values_.at(i).toDouble(), display) - scaled_diff, display)); } NodeKeyframe *tip_item = GetSelectedKeyframes().first(); - FloatSlider::DisplayType display_type = FloatSlider::kNormal; - Node* initial_drag_input = tip_item->parent(); - const QString& initial_drag_input_id = tip_item->input(); - if (initial_drag_input->HasInputProperty(initial_drag_input_id, QStringLiteral("view"))) { - display_type = static_cast(initial_drag_input->GetInputProperty(initial_drag_input_id, QStringLiteral("view")).toInt()); - } bool ok; double num_value = tip_item->value().toDouble(&ok); if (ok) { tip = QStringLiteral("%1\n"); - tip.append(FloatSlider::ValueToString(num_value, display_type, 2, true)); + tip.append(FloatSlider::ValueToString(num_value + GetOffsetFromKeyframe(tip_item), GetFloatDisplayTypeFromKeyframe(tip_item), 2, true)); } } @@ -458,7 +474,9 @@ void CurveView::KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *comman { for (int i=0; iadd_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i))); + if (!qFuzzyCompare(k->value().toDouble(), drag_keyframe_values_.at(i).toDouble())) { + command->add_child(new NodeParamSetKeyframeValueCommand(k, k->value(), drag_keyframe_values_.at(i))); + } } } @@ -488,53 +506,88 @@ QPointF CurveView::GetScaledCursorPos(const QPointF &cursor_pos) cursor_pos.y() / GetYScale()); } -void CurveView::ZoomToFitInternal(const QVector &keys) +void CurveView::ZoomToFitInternal(bool selected_only) { - if (keys.isEmpty()) { - // Prevent scaling to DBL_MIN/DBL_MAX - return; + bool got_val = false; + + rational min_time, max_time; + double min_val, max_val; + + foreach (KeyframeViewInputConnection *con, track_connections_) { + foreach (NodeKeyframe *key, con->GetKeyframes()) { + if (!selected_only || IsKeyframeSelected(key)) { + rational transformed_time = GetAdjustedTime(key->parent(), + GetTimeTarget(), + key->time(), + false); + + qreal key_y = GetUnscaledItemYFromKeyframeValue(key); + + if (got_val) { + min_time = qMin(transformed_time, min_time); + max_time = qMax(transformed_time, max_time); + + min_val = qMin(key_y, min_val); + max_val = qMax(key_y, max_val); + } else { + min_time = transformed_time; + max_time = transformed_time; + + min_val = key_y; + max_val = key_y; + + got_val = true; + } + } + } } - rational min_time = RATIONAL_MAX; - rational max_time = RATIONAL_MIN; + // Prevent scaling if no keyframes were found + if (got_val) { + QRectF desired(QPointF(min_time.toDouble(), min_val), QPointF(max_time.toDouble(), max_val)); - double min_val = DBL_MAX; - double max_val = DBL_MIN; + const double scale_divider = 0.5; + double scale_half_divider = scale_divider*0.5; - foreach (NodeKeyframe* key, keys) { - rational transformed_time = GetAdjustedTime(key->parent(), - GetTimeTarget(), - key->time(), - false); + double new_x_scale = viewport()->width() / desired.width() * scale_divider; + double new_y_scale; - min_time = qMin(transformed_time, min_time); - max_time = qMax(transformed_time, max_time); + if (qFuzzyIsNull(desired.height())) { + // Catch divide by zero + new_y_scale = 1.0; + scale_half_divider = 0.5; + } else { + // Use height as normal + new_y_scale = viewport()->height() / desired.height() * scale_divider; + } - min_val = qMin(key->value().toDouble(), min_val); - max_val = qMax(key->value().toDouble(), max_val); + emit ScaleChanged(new_x_scale); + SetYScale(new_y_scale); + + UpdateSceneRect(); + + int sb_x = desired.left() * new_x_scale - viewport()->width() * scale_half_divider; + QMetaObject::invokeMethod(horizontalScrollBar(), "setValue", Qt::QueuedConnection, Q_ARG(int, sb_x)); + + int sb_y = desired.top() * new_y_scale - viewport()->height() * scale_half_divider; + QMetaObject::invokeMethod(verticalScrollBar(), "setValue", Qt::QueuedConnection, Q_ARG(int, sb_y)); } - - double time_range = max_time.toDouble() - min_time.toDouble(); - double new_x_scale = CalculateScaleFromDimensions(this->width(), time_range); - double new_y_scale = CalculateScaleFromDimensions(this->height(), max_val - min_val); - - emit ScaleChanged(new_x_scale); - SetYScale(new_y_scale); - - QMetaObject::invokeMethod(horizontalScrollBar(), "setValue", Qt::QueuedConnection, - Q_ARG(int, TimeToScene(min_time) - CalculatePaddingFromDimensionScale(this->width()))); - QMetaObject::invokeMethod(verticalScrollBar(), "setValue", Qt::QueuedConnection, - Q_ARG(int, GetItemYFromKeyframeValue(max_val) - CalculatePaddingFromDimensionScale(this->height()))); } qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key) { - return GetItemYFromKeyframeValue(key->value().toDouble()); + return GetUnscaledItemYFromKeyframeValue(key) * GetYScale(); } -qreal CurveView::GetItemYFromKeyframeValue(double value) +qreal CurveView::GetUnscaledItemYFromKeyframeValue(NodeKeyframe *key) { - return -value * GetYScale(); + double val = key->value().toDouble(); + + val = FloatSlider::TransformValueToDisplay(val, GetFloatDisplayTypeFromKeyframe(key)); + + val += GetOffsetFromKeyframe(key); + + return -val; } QPointF CurveView::ScalePoint(const QPointF &point) @@ -543,41 +596,48 @@ QPointF CurveView::ScalePoint(const QPointF &point) return QPointF(point.x() * GetScale(), - point.y() * GetYScale()); } +FloatSlider::DisplayType CurveView::GetFloatDisplayTypeFromKeyframe(NodeKeyframe *key) +{ + Node* node = key->parent(); + const QString& input = key->input(); + if (node->HasInputProperty(input, QStringLiteral("view"))) { + // Try to get view from input (which will be normal if unset) + return static_cast(node->GetInputProperty(input, QStringLiteral("view")).toInt()); + } + + // Fallback to normal + return FloatSlider::kNormal; +} + +double CurveView::GetOffsetFromKeyframe(NodeKeyframe *key) +{ + Node *node = key->parent(); + const QString &input = key->input(); + if (node->HasInputProperty(input, QStringLiteral("offset"))) { + QVariant v = node->GetInputProperty(input, QStringLiteral("offset")); + + // NOTE: Implement getting correct offset for the track based on the data type + QVector track_vals = NodeValue::split_normal_value_into_track_values(node->GetInputDataType(input), v); + + return track_vals.at(key->track()).toDouble(); + } + + return 0; +} + QPointF CurveView::GetKeyframePosition(NodeKeyframe *key) { return QPointF(GetKeyframeSceneX(key), GetItemYFromKeyframeValue(key)); } -void CurveView::KeyframeTypeChanged() -{ - qDebug() << "STUB!"; - /*NodeKeyframe* key = static_cast(sender()); - KeyframeViewItem* item = item_map().value(key); - - if (item->isSelected()) { - item->setSelected(false); - item->setSelected(true); - }*/ -} - void CurveView::ZoomToFit() { - QVector keys; - - foreach (KeyframeViewInputConnection *con, track_connections_) { - foreach (NodeKeyframe *k, con->GetKeyframes()) { - if (!keys.contains(k)) { - keys.append(k); - } - } - } - - ZoomToFitInternal(keys); + ZoomToFitInternal(false); } void CurveView::ZoomToFitSelected() { - ZoomToFitInternal(GetSelectedKeyframes()); + ZoomToFitInternal(true); } void CurveView::ResetZoom() diff --git a/app/widget/curvewidget/curveview.h b/app/widget/curvewidget/curveview.h index 3858368e8..1eb8355f1 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -23,6 +23,7 @@ #include "node/keyframe.h" #include "widget/keyframeview/keyframeview.h" +#include "widget/slider/floatslider.h" namespace olive { @@ -38,8 +39,6 @@ public: void SelectKeyframesOfInput(const NodeKeyframeTrackReference &ref); - void ZoomToFitInput(const NodeKeyframeTrackReference &ref); - void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color); public slots: @@ -70,13 +69,17 @@ protected: virtual void KeyframeDragRelease(QMouseEvent *event, MultiUndoCommand *command) override; private: - void ZoomToFitInternal(const QVector &keys); + void ZoomToFitInternal(bool selected_only); qreal GetItemYFromKeyframeValue(NodeKeyframe* key); - qreal GetItemYFromKeyframeValue(double value); + qreal GetUnscaledItemYFromKeyframeValue(NodeKeyframe* key); QPointF ScalePoint(const QPointF& point); + static FloatSlider::DisplayType GetFloatDisplayTypeFromKeyframe(NodeKeyframe *key); + + static double GetOffsetFromKeyframe(NodeKeyframe *key); + void AdjustLines(); QPointF GetKeyframePosition(NodeKeyframe *key); @@ -112,9 +115,6 @@ private: QVector drag_keyframe_values_; -private slots: - void KeyframeTypeChanged(); - }; } diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 6dac944dd..e32f75d67 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -47,10 +47,7 @@ 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); - connect(tree_view_, &NodeTreeView::InputDoubleClicked, this, &CurveWidget::InputDoubleClicked); splitter->addWidget(tree_view_); QWidget* workarea = new QWidget(); @@ -101,7 +98,7 @@ CurveWidget::CurveWidget(QWidget *parent) : // Connect ruler and view together connect(view_, &CurveView::TimeChanged, this, &CurveWidget::SetTimeAndSignal); - connect(view_->scene(), &QGraphicsScene::selectionChanged, this, &CurveWidget::SelectionChanged); + connect(view_, &CurveView::SelectionChanged, this, &CurveWidget::SelectionChanged); connect(view_, &CurveView::ScaleChanged, this, &CurveWidget::SetScale); connect(view_, &CurveView::Dragged, this, &CurveWidget::KeyframeViewDragged); @@ -176,6 +173,8 @@ void CurveWidget::ScaleChangedEvent(const double &scale) void CurveWidget::TimeTargetChangedEvent(Node *target) { + TimeTargetObject::TimeTargetChangedEvent(target); + key_control_->SetTimeTarget(target); view_->SetTimeTarget(target); @@ -183,6 +182,8 @@ void CurveWidget::TimeTargetChangedEvent(Node *target) void CurveWidget::ConnectedNodeChangeEvent(ViewerOutput *n) { + super::ConnectedNodeChangeEvent(n); + SetTimeTarget(n); } @@ -215,7 +216,7 @@ void CurveWidget::UpdateBridgeTime(const rational &time) void CurveWidget::ConnectNode(Node *node, bool connect) { foreach (const QString& input, node->inputs()) { - if (node->IsInputKeyframable(input)) { + if (node->IsInputKeyframable(input) && !node->IsInputHidden(input)) { ConnectInput(node, input, connect); } } @@ -228,9 +229,6 @@ void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect) return; } - int track_count = NodeValue::get_number_of_keyframe_tracks(node->GetInputDataType(input)); - bool multiple_tracks = track_count > 1; - int arr_sz = node->InputArraySize(input); for (int i=-1; iSetKeyframeTrackColor(ref, c); } } - - if (tree_view_->IsInputEnabled(NodeKeyframeTrackReference(NodeInput(node, input, i), multiple_tracks ? -1 : 0))) { - if (multiple_tracks) { - for (int j=0; jIsInputEnabled(ref)) { - if (connect) { - view_->ConnectInput(ref); - } else { - view_->DisconnectInput(ref); - } - } - } - } else { - NodeKeyframeTrackReference ref(NodeInput(node, input, i), 0); - if (connect) { - view_->ConnectInput(ref); - } else { - view_->DisconnectInput(ref); - } - } - } } } @@ -339,32 +315,35 @@ void CurveWidget::KeyframeTypeButtonTriggered(bool checked) Core::instance()->undo_stack()->push(command); } -void CurveWidget::NodeEnabledChanged(Node* n, bool e) -{ - ConnectNode(n, e); -} - -void CurveWidget::InputEnabledChanged(const NodeKeyframeTrackReference& ref, bool e) -{ - if (e) { - view_->ConnectInput(ref); - } else { - view_->DisconnectInput(ref); - } -} - void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref) { key_control_->SetInput(ref.input()); - if (ref.IsValid()) { - view_->SelectKeyframesOfInput(ref); + foreach (const NodeKeyframeTrackReference &c, selected_tracks_) { + view_->DisconnectInput(c); } -} -void CurveWidget::InputDoubleClicked(const NodeKeyframeTrackReference& ref) -{ - view_->ZoomToFitInput(ref); + selected_tracks_.clear(); + + if (ref.IsValid()) { + view_->ConnectInput(ref); + selected_tracks_.append(ref); + } else if (ref.input().IsValid()) { + int track_count = NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()); + for (int i=0; iConnectInput(track_ref); + selected_tracks_.append(track_ref); + } + } else if (Node *node = ref.input().node()) { + foreach (const QString &input, node->inputs()) { + if (!node->IsInputKeyframable(input) || node->IsInputHidden(input)) { + + } + } + } + + view_->ZoomToFit(); } void CurveWidget::KeyframeViewDragged(int x, int y) diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index db14f2955..c27057fc8 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -96,19 +96,15 @@ private: QVector nodes_; + QVector selected_tracks_; + private slots: void SelectionChanged(); void KeyframeTypeButtonTriggered(bool checked); - void NodeEnabledChanged(Node* n, bool e); - - void InputEnabledChanged(const NodeKeyframeTrackReference &ref, bool e); - void InputSelectionChanged(const NodeKeyframeTrackReference& ref); - void InputDoubleClicked(const NodeKeyframeTrackReference& ref); - void KeyframeViewDragged(int x, int y); void CatchUpYScrollToPoint(int point); diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index cec7aca99..0e751265a 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -116,6 +116,7 @@ void KeyframeView::RemoveKeyframesOfTrack(KeyframeViewInputConnection *connectio } delete connection; Redraw(); + emit SelectionChanged(); } } @@ -157,6 +158,8 @@ void KeyframeView::SelectionManagerSelectEvent(void *obj) } } } + + emit SelectionChanged(); } void KeyframeView::SelectionManagerDeselectEvent(void *obj) @@ -170,6 +173,8 @@ void KeyframeView::SelectionManagerDeselectEvent(void *obj) } } } + + emit SelectionChanged(); } void KeyframeView::mousePressEvent(QMouseEvent *event) @@ -235,6 +240,7 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent *event) } else if (selection_manager_.IsRubberBanding()) { selection_manager_.RubberBandStop(); Redraw(); + emit SelectionChanged(); } } @@ -325,6 +331,8 @@ void KeyframeView::SelectKeyframe(NodeKeyframe *key) { if (selection_manager_.Select(key)) { Redraw(); + + emit SelectionChanged(); } } @@ -332,6 +340,8 @@ void KeyframeView::DeselectKeyframe(NodeKeyframe *key) { if (selection_manager_.Deselect(key)) { Redraw(); + + emit SelectionChanged(); } } diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index a0c1aa99a..09565f1b2 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -75,6 +75,8 @@ public: signals: void Dragged(int current_x, int current_y); + void SelectionChanged(); + protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; diff --git a/app/widget/keyframeview/keyframeviewinputconnection.cpp b/app/widget/keyframeview/keyframeviewinputconnection.cpp index b7b3c2dca..c0d2c3f31 100644 --- a/app/widget/keyframeview/keyframeviewinputconnection.cpp +++ b/app/widget/keyframeview/keyframeviewinputconnection.cpp @@ -38,6 +38,7 @@ KeyframeViewInputConnection::KeyframeViewInputConnection(const NodeKeyframeTrack connect(n, &Node::KeyframeRemoved, this, &KeyframeViewInputConnection::RemoveKeyframe); connect(n, &Node::KeyframeTimeChanged, this, &KeyframeViewInputConnection::KeyframeChanged); connect(n, &Node::KeyframeTypeChanged, this, &KeyframeViewInputConnection::KeyframeChanged); + connect(n, &Node::KeyframeTypeChanged, this, &KeyframeViewInputConnection::KeyframeTypeChanged); connect(n, &Node::KeyframeValueChanged, this, &KeyframeViewInputConnection::KeyframeChanged); } @@ -89,4 +90,11 @@ void KeyframeViewInputConnection::KeyframeChanged(NodeKeyframe *key) } } +void KeyframeViewInputConnection::KeyframeTypeChanged(NodeKeyframe *key) +{ + if (key->key_track_ref() == input_) { + emit TypeChanged(); + } +} + } diff --git a/app/widget/keyframeview/keyframeviewinputconnection.h b/app/widget/keyframeview/keyframeviewinputconnection.h index f10f37d2b..b6b0c1da5 100644 --- a/app/widget/keyframeview/keyframeviewinputconnection.h +++ b/app/widget/keyframeview/keyframeviewinputconnection.h @@ -65,6 +65,8 @@ public: signals: void RequireUpdate(); + void TypeChanged(); + private: KeyframeView *keyframe_view_; @@ -83,6 +85,8 @@ private slots: void KeyframeChanged(NodeKeyframe *key); + void KeyframeTypeChanged(NodeKeyframe *key); + }; } diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index e21ead255..697067343 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -417,11 +417,15 @@ void NodeParamView::SortItemsInContext(NodeParamViewContext *context_item) QVector > distances; for (auto it=context_item->GetItems().cbegin(); it!=context_item->GetItems().cend(); it++) { - int distance = 0; + int distance = -1; foreach (Node *ctx, context_item->GetContexts()) { distance = qMax(distance, GetDistanceBetweenNodes(ctx, it.key())); } + if (distance == -1) { + distance = INT_MAX; + } + bool inserted = false; QPair dist(it.value(), distance); @@ -439,6 +443,7 @@ void NodeParamView::SortItemsInContext(NodeParamViewContext *context_item) } foreach (auto info, distances) { + qDebug() << "Inserting" << info.first->GetNode() << "with distance" << info.second; context_item->GetDockArea()->AddItem(info.first); } } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index dda919eee..854d4a15f 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -648,42 +648,12 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr break; } } else if (key == QStringLiteral("offset")) { - switch (data_type) { - case NodeValue::kInt: - static_cast(widgets_.first())->SetOffset(value); - break; - case NodeValue::kFloat: - static_cast(widgets_.first())->SetOffset(value); - break; - case NodeValue::kRational: - static_cast(widgets_.first())->SetOffset(value); - break; - case NodeValue::kVec2: - { - QVector2D offs = value.value(); - static_cast(widgets_.at(0))->SetOffset(offs.x()); - static_cast(widgets_.at(1))->SetOffset(offs.y()); - break; - } - case NodeValue::kVec3: - { - QVector3D offs = value.value(); - static_cast(widgets_.at(0))->SetOffset(offs.x()); - static_cast(widgets_.at(1))->SetOffset(offs.y()); - static_cast(widgets_.at(2))->SetOffset(offs.z()); - break; - } - case NodeValue::kVec4: - { - QVector4D offs = value.value(); - static_cast(widgets_.at(0))->SetOffset(offs.x()); - static_cast(widgets_.at(1))->SetOffset(offs.y()); - static_cast(widgets_.at(2))->SetOffset(offs.z()); - static_cast(widgets_.at(3))->SetOffset(offs.w()); - break; - } - default: - break; + int tracks = NodeValue::get_number_of_keyframe_tracks(data_type); + + QVector offsets = NodeValue::split_normal_value_into_track_values(data_type, value); + + for (int i=0; i(widgets_.at(i))->SetOffset(offsets.at(i)); } UpdateWidgetValues(); diff --git a/app/widget/nodetreeview/nodetreeview.cpp b/app/widget/nodetreeview/nodetreeview.cpp index a2f8ec9df..fd0afc2d9 100644 --- a/app/widget/nodetreeview/nodetreeview.cpp +++ b/app/widget/nodetreeview/nodetreeview.cpp @@ -27,7 +27,8 @@ namespace olive { NodeTreeView::NodeTreeView(QWidget *parent) : QTreeWidget(parent), only_show_keyframable_(false), - show_keyframe_tracks_as_rows_(false) + show_keyframe_tracks_as_rows_(false), + checkboxes_enabled_(false) { connect(this, &NodeTreeView::itemChanged, this, &NodeTreeView::ItemCheckStateChanged); connect(this, &NodeTreeView::itemSelectionChanged, this, &NodeTreeView::SelectionChanged); @@ -67,12 +68,14 @@ void NodeTreeView::SetNodes(const QVector &nodes) foreach (Node* n, nodes_) { QTreeWidgetItem* node_item = new QTreeWidgetItem(); node_item->setText(0, n->Name()); - node_item->setCheckState(0, disabled_nodes_.contains(n) ? Qt::Unchecked : Qt::Checked); + if (checkboxes_enabled_) { + node_item->setCheckState(0, disabled_nodes_.contains(n) ? Qt::Unchecked : Qt::Checked); + } node_item->setData(0, kItemType, kItemTypeNode); node_item->setData(0, kItemNodePointer, Node::PtrToValue(n)); foreach (const QString& input, n->inputs()) { - if (only_show_keyframable_ && !n->IsInputKeyframable(input)) { + if (n->IsInputHidden(input) || (only_show_keyframable_ && !n->IsInputKeyframable(input))) { continue; } @@ -105,7 +108,6 @@ void NodeTreeView::SetNodes(const QVector &nodes) CreateItemsForTracks(element_item, input_ref, key_tracks.size()); } } - } // Add at the end to prevent unnecessary signalling while we're setting these objects up @@ -114,9 +116,9 @@ void NodeTreeView::SetNodes(const QVector &nodes) } else { delete node_item; } - - node_item->setExpanded(true); } + + expandAll(); } void NodeTreeView::changeEvent(QEvent *e) @@ -155,6 +157,8 @@ NodeKeyframeTrackReference NodeTreeView::GetSelectedInput() if (item->data(0, kItemType).toInt() == kItemTypeInput) { selected_ref = item->data(0, kItemInputReference).value(); + } else { + selected_ref = NodeKeyframeTrackReference(NodeInput(Node::ValueToPtr(item->data(0, kItemNodePointer)), QString())); } } @@ -166,21 +170,27 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKey QTreeWidgetItem* input_item = new QTreeWidgetItem(parent); QString item_name; - if (ref.track() == -1 || NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1) { - item_name = ref.input().name(); + if (ref.track() == -1 + || NodeValue::get_number_of_keyframe_tracks(ref.input().GetDataType()) == 1 + || (ref.input().IsArray() && ref.input().element() == -1)) { + if (ref.input().element() == -1) { + item_name = ref.input().name(); + } else { + item_name = QString::number(ref.input().element()); + } } else { switch (ref.track()) { case 0: - item_name = tr("X"); + item_name = UseRGBAOverXYZW(ref) ? tr("R") : tr("X"); break; case 1: - item_name = tr("Y"); + item_name = UseRGBAOverXYZW(ref) ? tr("G") : tr("Y"); break; case 2: - item_name = tr("Z"); + item_name = UseRGBAOverXYZW(ref) ? tr("B") : tr("Z"); break; case 3: - item_name = tr("W"); + item_name = UseRGBAOverXYZW(ref) ? tr("A") : tr("W"); break; default: item_name = QString::number(ref.track()); @@ -188,7 +198,9 @@ QTreeWidgetItem* NodeTreeView::CreateItem(QTreeWidgetItem *parent, const NodeKey } input_item->setText(0, item_name); - input_item->setCheckState(0, disabled_inputs_.contains(ref) ? Qt::Unchecked : Qt::Checked); + if (checkboxes_enabled_) { + input_item->setCheckState(0, disabled_inputs_.contains(ref) ? Qt::Unchecked : Qt::Checked); + } input_item->setData(0, kItemType, kItemTypeInput); input_item->setData(0, kItemInputReference, QVariant::fromValue(ref)); @@ -208,6 +220,11 @@ void NodeTreeView::CreateItemsForTracks(QTreeWidgetItem *parent, const NodeInput } } +bool NodeTreeView::UseRGBAOverXYZW(const NodeKeyframeTrackReference &ref) +{ + return ref.input().GetDataType() == NodeValue::kColor; +} + void NodeTreeView::ItemCheckStateChanged(QTreeWidgetItem *item, int column) { Q_UNUSED(column) diff --git a/app/widget/nodetreeview/nodetreeview.h b/app/widget/nodetreeview/nodetreeview.h index 3528b2822..067a4c82a 100644 --- a/app/widget/nodetreeview/nodetreeview.h +++ b/app/widget/nodetreeview/nodetreeview.h @@ -37,6 +37,11 @@ public: bool IsInputEnabled(const NodeKeyframeTrackReference& ref) const; + void SetCheckBoxesEnabled(bool e) + { + checkboxes_enabled_ = e; + } + void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color); void SetOnlyShowKeyframable(bool e) @@ -75,6 +80,8 @@ private: void CreateItemsForTracks(QTreeWidgetItem* parent, const NodeInput& input, int track_count); + static bool UseRGBAOverXYZW(const NodeKeyframeTrackReference &ref); + enum ItemType { kItemTypeNode, kItemTypeInput @@ -98,6 +105,8 @@ private: QHash keyframe_colors_; + bool checkboxes_enabled_; + private slots: void ItemCheckStateChanged(QTreeWidgetItem* item, int column); diff --git a/app/widget/slider/floatslider.cpp b/app/widget/slider/floatslider.cpp index 39c00f686..7510c36ef 100644 --- a/app/widget/slider/floatslider.cpp +++ b/app/widget/slider/floatslider.cpp @@ -78,29 +78,46 @@ void FloatSlider::SetDisplayType(const FloatSlider::DisplayType &type) } } -QString FloatSlider::ValueToString(double val, FloatSlider::DisplayType display, int decimal_places, bool autotrim_decimal_places) +double FloatSlider::TransformValueToDisplay(double val, DisplayType display) { switch (display) { case kNormal: - // Do nothing, skip to the return string at the end break; case kDecibel: - // Convert to decibels and return dB formatted string - - // Return negative infinity for zero volume - if (qIsNull(val)) { - return tr("\xE2\x88\x9E"); - } - val = Decibel::fromLinear(val); break; case kPercentage: - // Multiply value by 100 for user-friendly percentage val *= 100.0; break; } - return FloatToString(val, decimal_places, autotrim_decimal_places); + return val; +} + +double FloatSlider::TransformDisplayToValue(double val, DisplayType display) +{ + switch (display) { + case kNormal: + break; + case kDecibel: + val = Decibel::toLinear(val); + break; + case kPercentage: + val *= 0.01; + break; + } + + return val; +} + +QString FloatSlider::ValueToString(double val, FloatSlider::DisplayType display, int decimal_places, bool autotrim_decimal_places) +{ + // Return negative infinity for zero volume + if (display == kDecibel && qIsNull(val)) { + return tr("\xE2\x88\x9E"); + } + + return FloatToString(TransformValueToDisplay(val, display), decimal_places, autotrim_decimal_places); } QString FloatSlider::ValueToString(const QVariant &v) const @@ -110,46 +127,21 @@ QString FloatSlider::ValueToString(const QVariant &v) const QVariant FloatSlider::StringToValue(const QString &s, bool *ok) const { - switch (display_type_) { - case kNormal: - // Do nothing, skip to the return string at the end - break; - case kDecibel: - { - bool valid; + bool valid; + double val = s.toDouble(&valid); - // See if we can get a decimal number out of this - qreal decibels = s.toDouble(&valid); - - if (ok) *ok = valid; - - if (valid) { - // Convert from decibel scale to linear decimal - return Decibel::toLinear(decibels); - } - - break; - } - case kPercentage: - { - bool valid; - - // Try to get double value - double val = s.toDouble(&valid); - - if (ok) *ok = valid; - - // If we could get it, convert back to a 0.0 - 1.0 value and return - if (valid) { - return val * 0.01; - } - - break; - } + // If we were given an `ok` pointer, set it to `valid` + if (ok) { + *ok = valid; } - // Just try to convert the string to a double - return s.toDouble(ok) - GetOffset().toDouble(); + // If valid, transform it from display + if (valid) { + val = TransformDisplayToValue(val, display_type_); + } + + // Return un-offset value + return val - GetOffset().toDouble(); } QVariant FloatSlider::AdjustDragDistanceInternal(const QVariant &start, const double &drag) const diff --git a/app/widget/slider/floatslider.h b/app/widget/slider/floatslider.h index 4000e41bd..fe392a983 100644 --- a/app/widget/slider/floatslider.h +++ b/app/widget/slider/floatslider.h @@ -49,6 +49,10 @@ public: void SetDisplayType(const DisplayType& type); + static double TransformValueToDisplay(double val, DisplayType display); + + static double TransformDisplayToValue(double val, DisplayType display); + static QString ValueToString(double val, DisplayType display, int decimal_places, bool autotrim_decimal_places); protected: diff --git a/app/widget/timebased/timebasedview.h b/app/widget/timebased/timebasedview.h index f6f46d285..a51728afb 100644 --- a/app/widget/timebased/timebasedview.h +++ b/app/widget/timebased/timebasedview.h @@ -62,6 +62,11 @@ public slots: void SetEndTime(const rational& length); + /** + * @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes + */ + void UpdateSceneRect(); + signals: void TimeChanged(const rational& time); @@ -101,12 +106,6 @@ protected: y_axis_enabled_ = e; } -protected slots: - /** - * @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes - */ - void UpdateSceneRect(); - private: qreal GetPlayheadX(); diff --git a/app/widget/timebased/timebasedviewselectionmanager.h b/app/widget/timebased/timebasedviewselectionmanager.h index 19d61e203..ae5e32230 100644 --- a/app/widget/timebased/timebasedviewselectionmanager.h +++ b/app/widget/timebased/timebasedviewselectionmanager.h @@ -92,7 +92,9 @@ public: T *GetObjectAtPoint(const QPointF &scene_pt) { - foreach (const DrawnObject &kp, drawn_objects_) { + // Iterate in reverse order because the objects drawn later will appear on top to the user + for (auto it=drawn_objects_.crbegin(); it!=drawn_objects_.crend(); it++) { + const DrawnObject &kp = *it; if (kp.second.contains(scene_pt)) { return kp.first; } @@ -147,13 +149,11 @@ public: { initial_drag_item_ = initial_item; - dragging_.clear(); - dragging_.resize(selected_.size()); for (int i=0; itime()}; + dragging_[i] = {obj->time(), view_->TimeToScene(obj->time())}; } drag_mouse_start_ = view_->mapToScene(event->pos()); @@ -164,15 +164,15 @@ public: QPointF diff = view_->mapToScene(event->pos()) - drag_mouse_start_; for (int i=0; iSceneToTimeNoGrid(diff.x()); + rational proposed_time = view_->SceneToTimeNoGrid(dragging_.at(i).x + diff.x()); T *sel = selected_.at(i); // Magic number: use interval of 1ms to avoid collisions rational adj(1, 1000); - if (old_time < proposed_time) { + if (dragging_.at(i).time < proposed_time) { adj = -adj; } + while (true) { NodeKeyframe *key_at_time = sel->parent()->GetKeyframeAtTimeOnTrack(sel->input(), proposed_time, sel->track(), sel->element()); if (!key_at_time || key_at_time == sel) { @@ -303,6 +303,7 @@ private: struct DragObject { rational time; + double x; }; QVector dragging_;