From 7d82d56f31371ea096a898f73d2b9e43a4d833cc Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Mon, 13 Dec 2021 14:12:30 -0800 Subject: [PATCH] reworked much of keyframeview for new architecture --- CMakeLists.txt | 3 + app/panel/param/param.cpp | 1 - app/panel/param/param.h | 2 - app/widget/curvewidget/curveview.cpp | 154 +++--- app/widget/curvewidget/curveview.h | 18 +- app/widget/curvewidget/curvewidget.cpp | 43 +- app/widget/curvewidget/curvewidget.h | 6 - app/widget/keyframeview/CMakeLists.txt | 10 +- app/widget/keyframeview/keyframeview.cpp | 19 - app/widget/keyframeview/keyframeview.h | 8 +- app/widget/keyframeview/keyframeviewbase.cpp | 441 +++++++++--------- app/widget/keyframeview/keyframeviewbase.h | 75 +-- .../keyframeviewinputconnection.cpp | 83 ++++ .../keyframeviewinputconnection.h | 88 ++++ app/widget/keyframeview/keyframeviewitem.cpp | 129 ----- app/widget/keyframeview/keyframeviewitem.h | 68 --- app/widget/nodeparamview/nodeparamview.cpp | 132 ++---- app/widget/nodeparamview/nodeparamview.h | 8 - app/widget/nodeparamview/nodeparamviewitem.h | 13 + app/widget/timebased/CMakeLists.txt | 2 + .../timebasedviewselectionmanager.cpp | 26 ++ .../timebased/timebasedviewselectionmanager.h | 256 ++++++++++ app/widget/timebased/timescaledobject.cpp | 12 + app/widget/timebased/timescaledobject.h | 2 + app/window/mainwindow/mainwindow.cpp | 7 +- 25 files changed, 852 insertions(+), 754 deletions(-) create mode 100644 app/widget/keyframeview/keyframeviewinputconnection.cpp create mode 100644 app/widget/keyframeview/keyframeviewinputconnection.h delete mode 100644 app/widget/keyframeview/keyframeviewitem.cpp delete mode 100644 app/widget/keyframeview/keyframeviewitem.h create mode 100644 app/widget/timebased/timebasedviewselectionmanager.cpp create mode 100644 app/widget/timebased/timebasedviewselectionmanager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e8c28db2a..2b3ad5a73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,9 @@ else() endif() set(OLIVE_DEFINITIONS -DQT_DEPRECATED_WARNINGS) +if (WIN32) + list(APPEND OLIVE_DEFINITIONS -DUNICODE -D_UNICODE) +endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") diff --git a/app/panel/param/param.cpp b/app/panel/param/param.cpp index dc8f5ea45..bf44d5db3 100644 --- a/app/panel/param/param.cpp +++ b/app/panel/param/param.cpp @@ -29,7 +29,6 @@ ParamPanel::ParamPanel(QWidget* parent) : { NodeParamView* view = new NodeParamView(); connect(view, &NodeParamView::RequestSelectNode, this, &ParamPanel::RequestSelectNode); - connect(view, &NodeParamView::NodeOrderChanged, this, &ParamPanel::NodeOrderChanged); connect(view, &NodeParamView::FocusedNodeChanged, this, &ParamPanel::FocusedNodeChanged); SetTimeBasedWidget(view); diff --git a/app/panel/param/param.h b/app/panel/param/param.h index c6af767f9..e298ccf3e 100644 --- a/app/panel/param/param.h +++ b/app/panel/param/param.h @@ -48,8 +48,6 @@ public slots: signals: void RequestSelectNode(const QVector& target); - void NodeOrderChanged(const QVector& nodes); - void FocusedNodeChanged(Node* n); protected: diff --git a/app/widget/curvewidget/curveview.cpp b/app/widget/curvewidget/curveview.cpp index 80108a0d5..e83909702 100644 --- a/app/widget/curvewidget/curveview.cpp +++ b/app/widget/curvewidget/curveview.cpp @@ -47,22 +47,6 @@ CurveView::CurveView(QWidget *parent) : connect(scene(), &QGraphicsScene::selectionChanged, this, &CurveView::SelectionChanged); } -CurveView::~CurveView() -{ - // Quick way to avoid segfault when QGraphicsScene::selectionChanged is emitted after other members have been destroyed - Clear(); -} - -void CurveView::Clear() -{ - KeyframeViewBase::Clear(); - - foreach (QGraphicsLineItem* line, lines_) { - delete line; - } - lines_.clear(); -} - void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref) { if (connected_inputs_.contains(ref)) { @@ -71,7 +55,9 @@ void CurveView::ConnectInput(const NodeKeyframeTrackReference& ref) } // Add keyframes from track - AddKeyframesOfTrack(ref); + KeyframeViewInputConnection *track_con = AddKeyframesOfTrack(ref); + track_con->SetBrush(keyframe_colors_.value(ref)); + track_connections_.insert(ref, track_con); // Append to the list connected_inputs_.append(ref); @@ -85,7 +71,7 @@ void CurveView::DisconnectInput(const NodeKeyframeTrackReference& ref) } // Remove keyframes belonging to this element and track - RemoveKeyframesOfTrack(ref); + RemoveKeyframesOfTrack(track_connections_.take(ref)); // Remove from the list connected_inputs_.removeOne(ref); @@ -95,24 +81,16 @@ void CurveView::SelectKeyframesOfInput(const NodeKeyframeTrackReference& ref) { DeselectAll(); - for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->key_track_ref() == ref) { - it.value()->setSelected(true); + foreach (KeyframeViewInputConnection *con, track_connections_) { + foreach (NodeKeyframe *key, con->GetKeyframes()) { + SelectKeyframe(key); } } } void CurveView::ZoomToFitInput(const NodeKeyframeTrackReference& ref) { - QList keys; - - for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->key_track_ref() == ref) { - keys.append(it.key()); - } - } - - ZoomToFitInternal(keys); + ZoomToFitInternal(track_connections_.value(ref)->GetKeyframes()); } void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, const QColor &color) @@ -121,11 +99,7 @@ void CurveView::SetKeyframeTrackColor(const NodeKeyframeTrackReference &ref, con keyframe_colors_.insert(ref, color); // Update all keyframes - for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->key_track_ref() == ref) { - it.value()->SetOverrideBrush(color); - } - } + track_connections_.value(ref)->SetBrush(color); } void CurveView::drawBackground(QPainter *painter, const QRectF &rect) @@ -196,7 +170,7 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) QPainterPath path; // Draw straight line leading to first keyframe - QPointF first_key_pos = item_map().value(track.first())->pos(); + QPointF first_key_pos = GetKeyframePosition(track.first()); path.moveTo(QPointF(scene_bottom_left.x(), first_key_pos.y())); path.lineTo(first_key_pos); @@ -205,24 +179,24 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) NodeKeyframe* before = track.at(i-1); NodeKeyframe* after = track.at(i); - KeyframeViewItem* before_item = item_map().value(before); - KeyframeViewItem* after_item = item_map().value(after); + QPointF before_pos = GetKeyframePosition(before); + QPointF after_pos = GetKeyframePosition(after); if (before->type() == NodeKeyframe::kHold) { // Draw a hold keyframe (basically a right angle) - path.lineTo(after_item->pos().x(), before_item->pos().y()); - path.lineTo(after_item->pos().x(), after_item->pos().y()); + path.lineTo(after_pos.x(), before_pos.y()); + path.lineTo(after_pos.x(), after_pos.y()); } else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) { // Draw a cubic bezier // Cubic beziers have two control points, so we can just use both - QPointF before_control_point = before_item->pos() + ScalePoint(before->valid_bezier_control_out()); - QPointF after_control_point = after_item->pos() + ScalePoint(after->valid_bezier_control_in()); + QPointF before_control_point = before_pos + ScalePoint(before->valid_bezier_control_out()); + QPointF after_control_point = after_pos + ScalePoint(after->valid_bezier_control_in()); - path.cubicTo(before_control_point, after_control_point, after_item->pos()); + path.cubicTo(before_control_point, after_control_point, after_pos); } else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) { // Draw a quadratic bezier @@ -232,10 +206,10 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) QPointF control_point; if (before->type() == NodeKeyframe::kBezier) { - key_anchor = before_item->pos(); + key_anchor = before_pos; control_point = before->valid_bezier_control_out(); } else { - key_anchor = after_item->pos(); + key_anchor = after_pos; control_point = after->valid_bezier_control_in(); } @@ -243,18 +217,18 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) control_point = key_anchor + ScalePoint(control_point); // Create the path from both keyframes - path.quadTo(control_point, after_item->pos()); + path.quadTo(control_point, after_pos); } else { // Linear to linear - path.lineTo(after_item->pos()); + path.lineTo(after_pos); } } // Draw straight line leading from end keyframe - QPointF last_key_pos = item_map().value(track.last())->pos(); + QPointF last_key_pos = GetKeyframePosition(track.last()); path.lineTo(QPointF(scene_top_right.x(), last_key_pos.y())); painter->drawPath(path); @@ -263,7 +237,7 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) } // Draw bezier control point lines - if (!bezier_control_points_.isEmpty()) { + /*if (!bezier_control_points_.isEmpty()) { painter->setPen(QPen(palette().text().color(), 1)); QVector bezier_lines; @@ -274,13 +248,7 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect) bezier_lines.append(QLineF(par->pos(), par->pos() + item->pos())); } painter->drawLines(bezier_lines); - } -} - -void CurveView::KeyframeAboutToBeRemoved(NodeKeyframe *key) -{ - disconnect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged); - disconnect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged); + }*/ } void CurveView::ScaleChangedEvent(const double& scale) @@ -296,13 +264,11 @@ void CurveView::VerticalScaleChangedEvent(double scale) { Q_UNUSED(scale) - for (auto iterator=item_map().begin();iterator!=item_map().end();iterator++) { - SetItemYFromKeyframeValue(iterator.value()->key(), iterator.value()); - } - foreach (BezierControlPointItem* item, bezier_control_points_) { item->SetYScale(scale); } + + viewport()->update(); } void CurveView::ContextMenuEvent(Menu &m) @@ -324,7 +290,7 @@ void CurveView::SceneRectUpdateEvent(QRectF &r) r.setBottom(r.bottom() + this->height()); } -void CurveView::ZoomToFitInternal(const QList &keys) +void CurveView::ZoomToFitInternal(const QVector &keys) { if (keys.isEmpty()) { // Prevent scaling to DBL_MIN/DBL_MAX @@ -373,53 +339,58 @@ qreal CurveView::GetItemYFromKeyframeValue(double value) return -value * GetYScale(); } -void CurveView::SetItemYFromKeyframeValue(NodeKeyframe *key, KeyframeViewItem *item) -{ - item->SetOverrideY(GetItemYFromKeyframeValue(key)); -} - QPointF CurveView::ScalePoint(const QPointF &point) { // Flips Y coordinate because curves are drawn bottom to top return QPointF(point.x() * GetScale(), - point.y() * GetYScale()); } -void CurveView::CreateBezierControlPoints(KeyframeViewItem* item) +void CurveView::CreateBezierControlPoints(NodeKeyframe* item) { - BezierControlPointItem* bezier_in_pt = new BezierControlPointItem(item->key(), NodeKeyframe::kInHandle, item); + qDebug() << "STUB!"; + /*BezierControlPointItem* bezier_in_pt = new BezierControlPointItem(item, NodeKeyframe::kInHandle, item); bezier_in_pt->SetXScale(GetScale()); bezier_in_pt->SetYScale(GetYScale()); bezier_control_points_.append(bezier_in_pt); connect(bezier_in_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection); - BezierControlPointItem* bezier_out_pt = new BezierControlPointItem(item->key(), NodeKeyframe::kOutHandle, item); + BezierControlPointItem* bezier_out_pt = new BezierControlPointItem(item, NodeKeyframe::kOutHandle, item); bezier_out_pt->SetXScale(GetScale()); bezier_out_pt->SetYScale(GetYScale()); bezier_control_points_.append(bezier_out_pt); - connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection); + connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);*/ +} + +QPointF CurveView::GetKeyframePosition(NodeKeyframe *key) +{ + return QPointF(GetKeyframeSceneX(key), GetItemYFromKeyframeValue(key)); } void CurveView::KeyframeValueChanged() { - NodeKeyframe* key = static_cast(sender()); + qDebug() << "STUB!"; + /*NodeKeyframe* key = static_cast(sender()); KeyframeViewItem* item = item_map().value(key); - SetItemYFromKeyframeValue(key, item); + SetItemYFromKeyframeValue(key, item);*/ } void CurveView::KeyframeTypeChanged() { - NodeKeyframe* key = static_cast(sender()); + qDebug() << "STUB!"; + /*NodeKeyframe* key = static_cast(sender()); KeyframeViewItem* item = item_map().value(key); if (item->isSelected()) { item->setSelected(false); item->setSelected(true); - } + }*/ } void CurveView::SelectionChanged() { + qDebug() << "STUB!"; + /* // Clear current bezier handles while (!bezier_control_points_.isEmpty()) { delete bezier_control_points_.first(); @@ -434,6 +405,7 @@ void CurveView::SelectionChanged() CreateBezierControlPoints(this_item); } } + */ } void CurveView::BezierControlPointDestroyed() @@ -444,20 +416,22 @@ void CurveView::BezierControlPointDestroyed() void CurveView::ZoomToFit() { - ZoomToFitInternal(item_map().keys()); + QVector keys; + + foreach (KeyframeViewInputConnection *con, track_connections_) { + foreach (NodeKeyframe *k, con->GetKeyframes()) { + if (!keys.contains(k)) { + keys.append(k); + } + } + } + + ZoomToFitInternal(keys); } void CurveView::ZoomToFitSelected() { - QList selected_keys; - - for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.value()->isSelected()) { - selected_keys.append(it.key()); - } - } - - ZoomToFitInternal(selected_keys); + ZoomToFitInternal(GetSelectedKeyframes()); } void CurveView::ResetZoom() @@ -466,16 +440,4 @@ void CurveView::ResetZoom() SetYScale(1.0); } -KeyframeViewItem* CurveView::AddKeyframe(NodeKeyframe* key) -{ - KeyframeViewItem* item = super::AddKeyframe(key); - SetItemYFromKeyframeValue(key, item); - item->SetOverrideBrush(keyframe_colors_.value(key->key_track_ref())); - - connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged); - connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged); - - return item; -} - } diff --git a/app/widget/curvewidget/curveview.h b/app/widget/curvewidget/curveview.h index 6a4e155c7..e5b056830 100644 --- a/app/widget/curvewidget/curveview.h +++ b/app/widget/curvewidget/curveview.h @@ -24,7 +24,6 @@ #include "beziercontrolpointitem.h" #include "node/keyframe.h" #include "widget/keyframeview/keyframeview.h" -#include "widget/keyframeview/keyframeviewitem.h" namespace olive { @@ -34,10 +33,6 @@ class CurveView : public KeyframeViewBase public: CurveView(QWidget* parent = nullptr); - virtual ~CurveView() override; - - virtual void Clear() override; - void ConnectInput(const NodeKeyframeTrackReference &ref); void DisconnectInput(const NodeKeyframeTrackReference &ref); @@ -49,8 +44,6 @@ public: void SetKeyframeTrackColor(const NodeKeyframeTrackReference& ref, const QColor& color); public slots: - virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override; - void ZoomToFit(); void ZoomToFitSelected(); @@ -60,8 +53,6 @@ public slots: protected: virtual void drawBackground(QPainter* painter, const QRectF& rect) override; - virtual void KeyframeAboutToBeRemoved(NodeKeyframe *key) override; - virtual void ScaleChangedEvent(const double &scale) override; virtual void VerticalScaleChangedEvent(double scale) override; @@ -71,20 +62,21 @@ protected: virtual void SceneRectUpdateEvent(QRectF &r) override; private: - void ZoomToFitInternal(const QList &keys); + void ZoomToFitInternal(const QVector &keys); qreal GetItemYFromKeyframeValue(NodeKeyframe* key); qreal GetItemYFromKeyframeValue(double value); - void SetItemYFromKeyframeValue(NodeKeyframe* key, KeyframeViewItem* item); - QPointF ScalePoint(const QPointF& point); void AdjustLines(); - void CreateBezierControlPoints(KeyframeViewItem *item); + void CreateBezierControlPoints(NodeKeyframe *item); + + QPointF GetKeyframePosition(NodeKeyframe *key); QHash keyframe_colors_; + QHash track_connections_; int text_padding_; diff --git a/app/widget/curvewidget/curvewidget.cpp b/app/widget/curvewidget/curvewidget.cpp index 4de5787ef..c77a553aa 100644 --- a/app/widget/curvewidget/curvewidget.cpp +++ b/app/widget/curvewidget/curvewidget.cpp @@ -113,12 +113,6 @@ CurveWidget::CurveWidget(QWidget *parent) : SetScale(120.0); } -CurveWidget::~CurveWidget() -{ - // Quick way to avoid segfault when QGraphicsScene::selectionChanged is emitted after other members have been destroyed - view_->Clear(); -} - const double &CurveWidget::GetVerticalScale() { return view_->GetYScale(); @@ -223,15 +217,6 @@ void CurveWidget::ConnectNode(Node *node, bool connect) ConnectInput(node, input, connect); } } - - // Connect add/remove signals - if (connect) { - QObject::connect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe); - QObject::connect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); - } else { - QObject::disconnect(node, &Node::KeyframeAdded, this, &CurveWidget::AddKeyframe); - QObject::disconnect(node, &Node::KeyframeRemoved, this, &CurveWidget::RemoveKeyframe); - } } void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect) @@ -287,20 +272,20 @@ void CurveWidget::ConnectInput(Node *node, const QString &input, bool connect) void CurveWidget::SelectionChanged() { - QList selected = view_->scene()->selectedItems(); + const QVector &selected = view_->GetSelectedKeyframes(); SetKeyframeButtonChecked(false); SetKeyframeButtonEnabled(!selected.isEmpty()); if (!selected.isEmpty()) { bool all_same_type = true; - NodeKeyframe::Type type = static_cast(selected.first())->key()->type(); + NodeKeyframe::Type type = selected.first()->type(); for (int i=1;i(selected.at(i-1)); - KeyframeViewItem* this_item = static_cast(selected.at(i)); + NodeKeyframe* prev_item = selected.at(i-1); + NodeKeyframe* this_item = selected.at(i); - if (prev_item->key()->type() != this_item->key()->type()) { + if (prev_item->type() != this_item->type()) { all_same_type = false; break; } @@ -323,7 +308,7 @@ void CurveWidget::KeyframeTypeButtonTriggered(bool checked) } // Get selected items and do nothing if there are none - QList selected = view_->scene()->selectedItems(); + const QVector &selected = view_->GetSelectedKeyframes(); if (selected.isEmpty()) { return; } @@ -345,10 +330,8 @@ void CurveWidget::KeyframeTypeButtonTriggered(bool checked) MultiUndoCommand* command = new MultiUndoCommand(); - foreach (QGraphicsItem* item, selected) { - KeyframeViewItem* key_item = static_cast(item); - - command->add_child(new KeyframeSetTypeCommand(key_item->key(), new_type)); + foreach (NodeKeyframe* item, selected) { + command->add_child(new KeyframeSetTypeCommand(item, new_type)); } Core::instance()->undo_stack()->push(command); @@ -368,16 +351,6 @@ void CurveWidget::InputEnabledChanged(const NodeKeyframeTrackReference& ref, boo } } -void CurveWidget::AddKeyframe(NodeKeyframe *key) -{ - view_->AddKeyframe(key); -} - -void CurveWidget::RemoveKeyframe(NodeKeyframe *key) -{ - view_->RemoveKeyframe(key); -} - void CurveWidget::InputSelectionChanged(const NodeKeyframeTrackReference& ref) { key_control_->SetInput(ref.input()); diff --git a/app/widget/curvewidget/curvewidget.h b/app/widget/curvewidget/curvewidget.h index 7b9e01021..db14f2955 100644 --- a/app/widget/curvewidget/curvewidget.h +++ b/app/widget/curvewidget/curvewidget.h @@ -40,8 +40,6 @@ class CurveWidget : public TimeBasedWidget, public TimeTargetObject public: CurveWidget(QWidget* parent = nullptr); - virtual ~CurveWidget() override; - const double& GetVerticalScale(); void SetVerticalScale(const double& vscale); @@ -107,10 +105,6 @@ private slots: void InputEnabledChanged(const NodeKeyframeTrackReference &ref, bool e); - void AddKeyframe(NodeKeyframe* key); - - void RemoveKeyframe(NodeKeyframe* key); - void InputSelectionChanged(const NodeKeyframeTrackReference& ref); void InputDoubleClicked(const NodeKeyframeTrackReference& ref); diff --git a/app/widget/keyframeview/CMakeLists.txt b/app/widget/keyframeview/CMakeLists.txt index c4d5ef43e..1ab460182 100644 --- a/app/widget/keyframeview/CMakeLists.txt +++ b/app/widget/keyframeview/CMakeLists.txt @@ -16,13 +16,13 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} - widget/keyframeview/keyframeview.h widget/keyframeview/keyframeview.cpp - widget/keyframeview/keyframeviewbase.h + widget/keyframeview/keyframeview.h widget/keyframeview/keyframeviewbase.cpp - widget/keyframeview/keyframeviewitem.h - widget/keyframeview/keyframeviewitem.cpp - widget/keyframeview/keyframeviewundo.h + widget/keyframeview/keyframeviewbase.h + widget/keyframeview/keyframeviewinputconnection.cpp + widget/keyframeview/keyframeviewinputconnection.h widget/keyframeview/keyframeviewundo.cpp + widget/keyframeview/keyframeviewundo.h PARENT_SCOPE ) diff --git a/app/widget/keyframeview/keyframeview.cpp b/app/widget/keyframeview/keyframeview.cpp index bba5f29f9..f8cf5413f 100644 --- a/app/widget/keyframeview/keyframeview.cpp +++ b/app/widget/keyframeview/keyframeview.cpp @@ -31,29 +31,10 @@ KeyframeView::KeyframeView(QWidget *parent) : setAlignment(Qt::AlignLeft | Qt::AlignTop); } -void KeyframeView::SetElementY(const NodeInput &c, int y) -{ - qreal scene_y = mapToScene(mapFromGlobal(QPoint(0, y))).y(); - - element_y_.insert(c, scene_y); - - for (auto it=item_map().cbegin(); it!=item_map().cend(); it++) { - if (it.key()->key_track_ref().input() == c) { - it.value()->SetOverrideY(scene_y); - } - } -} void KeyframeView::SceneRectUpdateEvent(QRectF &rect) { rect.setY(0); rect.setHeight(max_scroll_); } -KeyframeViewItem* KeyframeView::AddKeyframe(NodeKeyframe* key) -{ - KeyframeViewItem* item = super::AddKeyframe(key); - item->SetOverrideY(element_y_.value(key->key_track_ref().input())); - return item; -} - } diff --git a/app/widget/keyframeview/keyframeview.h b/app/widget/keyframeview/keyframeview.h index 9b7f76d89..56874232a 100644 --- a/app/widget/keyframeview/keyframeview.h +++ b/app/widget/keyframeview/keyframeview.h @@ -34,19 +34,13 @@ public: void SetMaxScroll(int i) { max_scroll_ = i; + UpdateSceneRect(); } - void SetElementY(const NodeInput& c, int y); - protected: virtual void SceneRectUpdateEvent(QRectF& rect) override; -public slots: - virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key) override; - private: - QHash element_y_; - int max_scroll_; }; diff --git a/app/widget/keyframeview/keyframeviewbase.cpp b/app/widget/keyframeview/keyframeviewbase.cpp index 5d0384708..c3be1e273 100644 --- a/app/widget/keyframeview/keyframeviewbase.cpp +++ b/app/widget/keyframeview/keyframeviewbase.cpp @@ -24,6 +24,7 @@ #include #include +#include "common/qtutils.h" #include "dialog/keyframeproperties/keyframeproperties.h" #include "keyframeviewundo.h" #include "node/node.h" @@ -33,201 +34,144 @@ namespace olive { +#define super TimeBasedView + KeyframeViewBase::KeyframeViewBase(QWidget *parent) : - TimeBasedView(parent), + super(parent), dragging_bezier_point_(nullptr), currently_autoselecting_(false), - dragging_(false) + dragging_(false), + selection_manager_(this) { SetDefaultDragMode(RubberBandDrag); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &KeyframeViewBase::customContextMenuRequested, this, &KeyframeViewBase::ShowContextMenu); - connect(scene(), &QGraphicsScene::selectionChanged, this, &KeyframeViewBase::AutoSelectKeyTimeNeighbors); -} - -void KeyframeViewBase::Clear() -{ - QMap::iterator iterator; - - for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) { - delete iterator.value(); - } - - item_map_.clear(); } void KeyframeViewBase::DeleteSelected() { MultiUndoCommand* command = new MultiUndoCommand(); - QMap::const_iterator i; - - for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) { - if (i.value()->isSelected()) { - command->add_child(new NodeParamRemoveKeyframeCommand(i.key())); - } + foreach (NodeKeyframe *key, GetSelectedKeyframes()) { + command->add_child(new NodeParamRemoveKeyframeCommand(key)); } Core::instance()->undo_stack()->pushIfHasChildren(command); } -void KeyframeViewBase::AddKeyframesOfNode(Node *n) +KeyframeViewBase::NodeConnections KeyframeViewBase::AddKeyframesOfNode(Node *n) { + NodeConnections map; + foreach (const QString& i, n->inputs()) { - AddKeyframesOfInput(n, i); + map.insert(i, AddKeyframesOfInput(n, i)); } + + return map; } -void KeyframeViewBase::AddKeyframesOfInput(Node* n, const QString& input) +KeyframeViewBase::InputConnections KeyframeViewBase::AddKeyframesOfInput(Node* n, const QString& input) { - if (!n->IsInputKeyframable(input)) { - return; + InputConnections vec; + + if (n->IsInputKeyframable(input)) { + int arr_sz = n->InputArraySize(input); + vec.resize(arr_sz + 1); + for (int i=-1; iInputArraySize(input); - for (int i=-1; i& tracks = input.node()->GetKeyframeTracks(input); + ElementConnections vec(tracks.size()); for (int i=0; i& tracks = ref.input().node()->GetKeyframeTracks(ref.input()); - const NodeKeyframeTrack& t = tracks.at(ref.track()); - - foreach (NodeKeyframe* key, t) { - AddKeyframe(key); - } + KeyframeViewInputConnection *track = new KeyframeViewInputConnection(ref, this); + connect(track, &KeyframeViewInputConnection::RequireUpdate, this, &KeyframeViewBase::Redraw); + tracks_.append(track); + Redraw(); + return track; } -void KeyframeViewBase::RemoveKeyframesOfNode(Node *n) +void KeyframeViewBase::RemoveKeyframesOfTrack(KeyframeViewInputConnection *connection) { - foreach (const QString& i, n->inputs()) { - RemoveKeyframesOfInput(n, i); - } -} - -void KeyframeViewBase::RemoveKeyframesOfInput(Node* n, const QString& input) -{ - if (!n->IsInputKeyframable(input)) { - return; - } - - int arr_sz = n->InputArraySize(input); - for (int i=-1; i& tracks = input.node()->GetKeyframeTracks(input); - - for (int i=0; i& tracks = ref.input().node()->GetKeyframeTracks(ref.input()); - const NodeKeyframeTrack& t = tracks.at(ref.track()); - - foreach (NodeKeyframe* key, t) { - RemoveKeyframe(key); + if (tracks_.removeOne(connection)) { + delete connection; + Redraw(); } } void KeyframeViewBase::SelectAll() { - for (auto it=item_map_.cbegin(); it!=item_map_.cend(); it++) { - it.value()->setSelected(true); + foreach (KeyframeViewInputConnection *track, tracks_) { + foreach (NodeKeyframe *key, track->GetKeyframes()) { + SelectKeyframe(key); + } } } void KeyframeViewBase::DeselectAll() { - for (auto it=item_map_.cbegin(); it!=item_map_.cend(); it++) { - it.value()->setSelected(false); - } + selection_manager_.ClearSelection(); + + Redraw(); } -void KeyframeViewBase::RemoveKeyframe(NodeKeyframe* key) +void KeyframeViewBase::Clear() { - KeyframeAboutToBeRemoved(key); - - delete item_map_.take(key); -} - -KeyframeViewItem *KeyframeViewBase::AddKeyframe(NodeKeyframe* key) -{ - KeyframeViewItem* item = item_map_.value(key); - - if (!item) { - item = new KeyframeViewItem(key); - item->SetTimeTarget(GetTimeTarget()); - item->SetScale(GetScale()); - item_map_.insert(key, item); - scene()->addItem(item); + if (!tracks_.isEmpty()) { + qDeleteAll(tracks_); + tracks_.clear(); + Redraw(); } - - return item; } void KeyframeViewBase::mousePressEvent(QMouseEvent *event) { - QGraphicsItem* item_under_cursor = itemAt(event->pos()); + NodeKeyframe *key_under_cursor = selection_manager_.MousePress(event); + if (key_under_cursor) { + AutoSelectKeyTimeNeighbors(); + } - if (HandPress(event) || (!item_under_cursor && PlayheadPress(event))) { + BezierControlPointItem *bezier_under_cursor = dynamic_cast(itemAt(event->pos())); + + Redraw(); + + if (HandPress(event) || (!bezier_under_cursor && !key_under_cursor && PlayheadPress(event))) { return; } - active_tool_ = Core::instance()->tool(); - if (event->button() == Qt::LeftButton) { - QGraphicsView::mousePressEvent(event); + if (key_under_cursor || bezier_under_cursor) { + dragging_ = true; + drag_start_ = mapToScene(event->pos()); - if (active_tool_ == Tool::kPointer) { - if (item_under_cursor) { + // Determine what type of item is under the cursor + dragging_bezier_point_ = bezier_under_cursor; - dragging_ = true; - drag_start_ = mapToScene(event->pos()); + if (dragging_bezier_point_) { - // Determine what type of item is under the cursor - dragging_bezier_point_ = dynamic_cast(item_under_cursor); + dragging_bezier_point_start_ = dragging_bezier_point_->GetCorrespondingKeyframeHandle(); + dragging_bezier_point_opposing_start_ = dragging_bezier_point_->key()->bezier_control(NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode())); - if (dragging_bezier_point_) { + } else { - dragging_bezier_point_start_ = dragging_bezier_point_->GetCorrespondingKeyframeHandle(); - dragging_bezier_point_opposing_start_ = dragging_bezier_point_->key()->bezier_control(NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode())); + selection_manager_.DragStart(key_under_cursor, event); - } else { - - QList selected_items = scene()->selectedItems(); - - selected_keys_.resize(selected_items.size()); - - initial_drag_item_ = static_cast(item_under_cursor); - - for (int i=0;i(selected_items.at(i)); - - selected_keys_.replace(i, {key, - key->x(), - GetAdjustedTime(key->key()->parent(), GetTimeTarget(), key->key()->time(), false), - key->key()->value().toDouble()}); - } - } } } } @@ -240,11 +184,10 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) } if (event->buttons() & Qt::LeftButton) { - QGraphicsView::mouseMoveEvent(event); - if (dragging_) { // Calculate cursor difference and scale it - QPointF mouse_diff_scaled = GetScaledCursorPos(mapToScene(event->pos()) - drag_start_); + QPointF scene_pos = mapToScene(event->pos()); + QPointF mouse_diff_scaled = GetScaledCursorPos(scene_pos - drag_start_); if (event->modifiers() & Qt::ShiftModifier) { // If holding shift, only move one axis @@ -284,15 +227,19 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) QPointF bezier_pos = dragging_bezier_point_->pos() + dragging_bezier_point_->parentItem()->pos(); emit Dragged(qRound(bezier_pos.x()), qRound(bezier_pos.y())); - } else if (!selected_keys_.isEmpty()) { + } else if (selection_manager_.IsDragging()) { + QString tip; + + /* // Validate movement - ensure no keyframe goes above its max point or below its min point FloatSlider::DisplayType display_type = FloatSlider::kNormal; if (IsYAxisEnabled()) { - foreach (const KeyframeItemAndTime& keypair, selected_keys_) { - Node* node = keypair.key->key()->parent(); - const QString& input = keypair.key->key()->input(); + foreach (const KeyframeItemAndTime& keypair, dragging_keyframes_) { + NodeKeyframe *key = keypair.key; + Node* node = key->parent(); + const QString& input = key->input(); double new_val = keypair.value - mouse_diff_scaled.y(); double limited = new_val; @@ -309,48 +256,37 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event) } } - Node* initial_drag_input = initial_drag_item_->key()->parent(); - const QString& initial_drag_input_id = initial_drag_item_->key()->input(); + Node* initial_drag_input = initial_drag_item_->parent(); + const QString& initial_drag_input_id = initial_drag_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()); } } - foreach (const KeyframeItemAndTime& keypair, selected_keys_) { - rational node_time = GetAdjustedTime(GetTimeTarget(), - keypair.key->key()->parent(), - CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()), - true); - - keypair.key->key()->set_time(node_time); - + foreach (const KeyframeItemAndTime& keypair, dragging_keyframes_) { if (IsYAxisEnabled()) { - keypair.key->key()->set_value(keypair.value - mouse_diff_scaled.y()); + key->set_value(keypair.value - mouse_diff_scaled.y()); } } - // Show information about this keyframe - QString tip = Timecode::time_to_timecode(initial_drag_item_->key()->time(), timebase(), - Core::instance()->GetTimecodeDisplay(), false); + if (IsYAxisEnabled()) { bool ok; - double num_value = initial_drag_item_->key()->value().toDouble(&ok); + double num_value = initial_drag_item_->value().toDouble(&ok); if (ok) { - tip.append('\n'); + tip = QStringLiteral("%1\n"); tip.append(FloatSlider::ValueToString(num_value, display_type, 2, true)); } - - // Force viewport to update since Qt might try to optimize it out if the keyframe is - // offscreen - viewport()->update(); } + */ - QToolTip::hideText(); - QToolTip::showText(QCursor::pos(), tip); + selection_manager_.DragMove(event, tip); - emit Dragged(qRound(initial_drag_item_->x()), qRound(initial_drag_item_->y())); + Redraw(); + + emit Dragged(scene_pos.x(), scene_pos.y()); } } @@ -364,8 +300,6 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event) } if (event->button() == Qt::LeftButton) { - QGraphicsView::mouseReleaseEvent(event); - if (dragging_) { if (dragging_bezier_point_) { MultiUndoCommand* command = new MultiUndoCommand(); @@ -388,30 +322,19 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event) dragging_bezier_point_ = nullptr; Core::instance()->undo_stack()->push(command); - } else if (!selected_keys_.isEmpty()) { + } else if (selection_manager_.IsDragging()) { MultiUndoCommand* command = new MultiUndoCommand(); - foreach (const KeyframeItemAndTime& keypair, selected_keys_) { - NodeKeyframe* item = keypair.key->key(); - - // Commit movement - command->add_child(new NodeParamSetKeyframeTimeCommand(item, - item->time(), - keypair.time)); - - // Commit value if we're setting a value - if (IsYAxisEnabled()) { - command->add_child(new NodeParamSetKeyframeValueCommand(item, - item->value(), - keypair.value)); - } - } + selection_manager_.DragStop(command); + /*if (IsYAxisEnabled()) { + command->add_child(new NodeParamSetKeyframeValueCommand(item, + item->value(), + keypair.value)); + }*/ Core::instance()->undo_stack()->push(command); } - selected_keys_.clear(); - dragging_ = false; QToolTip::hideText(); @@ -419,31 +342,77 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event) } } +void KeyframeViewBase::drawForeground(QPainter *painter, const QRectF &rect) +{ + int key_sz = QtUtils::QFontMetricsWidth(fontMetrics(), "Oi"); + int key_rad = key_sz/2; + + selection_manager_.ClearDrawnObjects(); + + painter->setRenderHint(QPainter::Antialiasing); + + painter->setPen(Qt::black); + + foreach (KeyframeViewInputConnection *track, tracks_) { + foreach (NodeKeyframe *key, track->GetKeyframes()) { + QRectF key_rect(-key_rad, -key_rad, key_sz, key_sz); + key_rect.translate(GetKeyframeSceneX(key), mapFromGlobal(QPoint(0, track->GetKeyframeY())).y()); + + if (!rect.intersects(key_rect)) { + continue; + } + + if (IsKeyframeSelected(key)) { + painter->setBrush(palette().highlight()); + } else { + painter->setBrush(track->GetBrush()); + } + + selection_manager_.DeclareDrawnObject(key, key_rect); + + switch (key->type()) { + case NodeKeyframe::kLinear: + { + QPointF points[] = { + QPointF(key_rect.center().x(), key_rect.top()), + QPointF(key_rect.right(), key_rect.center().y()), + QPointF(key_rect.center().x(), key_rect.bottom()), + QPointF(key_rect.left(), key_rect.center().y()) + }; + + painter->drawPolygon(points, 4); + break; + } + case NodeKeyframe::kBezier: + painter->drawEllipse(key_rect); + break; + case NodeKeyframe::kHold: + painter->drawRect(key_rect); + break; + } + } + } + + super::drawForeground(painter, rect); +} + void KeyframeViewBase::ScaleChangedEvent(const double &scale) { - TimeBasedView::ScaleChangedEvent(scale); + super::ScaleChangedEvent(scale); - for (auto iterator=item_map_.begin();iterator!=item_map_.end();iterator++) { - iterator.value()->SetScale(scale); - } -} - -const QMap &KeyframeViewBase::item_map() const -{ - return item_map_; -} - -void KeyframeViewBase::KeyframeAboutToBeRemoved(NodeKeyframe *) -{ + Redraw(); } void KeyframeViewBase::TimeTargetChangedEvent(Node *target) { - QMap::const_iterator i; + Redraw(); +} - for (i=item_map_.begin();i!=item_map_.end();i++) { - i.value()->SetTimeTarget(target); - } +void KeyframeViewBase::TimebaseChangedEvent(const rational &timebase) +{ + super::TimebaseChangedEvent(timebase); + + selection_manager_.SetTimebase(timebase); } void KeyframeViewBase::ContextMenuEvent(Menu& m) @@ -451,6 +420,30 @@ void KeyframeViewBase::ContextMenuEvent(Menu& m) Q_UNUSED(m) } +void KeyframeViewBase::SelectKeyframe(NodeKeyframe *key) +{ + if (selection_manager_.Select(key)) { + Redraw(); + } +} + +void KeyframeViewBase::DeselectKeyframe(NodeKeyframe *key) +{ + if (selection_manager_.Deselect(key)) { + Redraw(); + } +} + +rational KeyframeViewBase::GetAdjustedKeyframeTime(NodeKeyframe *key) +{ + return GetAdjustedTime(key->parent(), GetTimeTarget(), key->time(), false); +} + +double KeyframeViewBase::GetKeyframeSceneX(NodeKeyframe *key) +{ + return TimeToScene(GetAdjustedKeyframeTime(key)); +} + rational KeyframeViewBase::CalculateNewTimeFromScreen(const rational &old_time, double cursor_diff) { return rational::fromDouble(old_time.toDouble() + cursor_diff); @@ -492,16 +485,15 @@ void KeyframeViewBase::ShowContextMenu() QAction* bezier_key_action = nullptr; QAction* hold_key_action = nullptr; - QList items = scene()->selectedItems(); - if (!items.isEmpty()) { + if (!GetSelectedKeyframes().isEmpty()) { bool all_keys_are_same_type = true; - NodeKeyframe::Type type = static_cast(items.first())->key()->type(); + NodeKeyframe::Type type = GetSelectedKeyframes().first()->type(); - for (int i=1;i(items.at(i)); - KeyframeViewItem* prev_item = static_cast(items.at(i-1)); + for (int i=1;ikey()->type() != prev_item->key()->type()) { + if (key_item->type() != prev_item->type()) { all_keys_are_same_type = false; break; } @@ -536,7 +528,7 @@ void KeyframeViewBase::ShowContextMenu() ContextMenuEvent(m); - if (!items.isEmpty()) { + if (!GetSelectedKeyframes().isEmpty()) { m.addSeparator(); QAction* properties_action = m.addAction(tr("P&roperties")); @@ -546,7 +538,7 @@ void KeyframeViewBase::ShowContextMenu() QAction* selected = m.exec(QCursor::pos()); // Process keyframe type changes - if (!items.isEmpty()) { + if (selected) { if (selected == linear_key_action || selected == bezier_key_action || selected == hold_key_action) { @@ -561,26 +553,18 @@ void KeyframeViewBase::ShowContextMenu() } MultiUndoCommand* command = new MultiUndoCommand(); - foreach (QGraphicsItem* item, items) { - command->add_child(new KeyframeSetTypeCommand(static_cast(item)->key(), - new_type)); + foreach (NodeKeyframe* item, GetSelectedKeyframes()) { + command->add_child(new KeyframeSetTypeCommand(item, new_type)); } - Core::instance()->undo_stack()->pushIfHasChildren(command); + Core::instance()->undo_stack()->push(command); } } } void KeyframeViewBase::ShowKeyframePropertiesDialog() { - QList items = scene()->selectedItems(); - QVector keys; - - foreach (QGraphicsItem* item, items) { - keys.append(static_cast(item)->key()); - } - - if (!keys.isEmpty()) { - KeyframePropertiesDialog kd(keys, timebase(), this); + if (!GetSelectedKeyframes().isEmpty()) { + KeyframePropertiesDialog kd(GetSelectedKeyframes(), timebase(), this); kd.exec(); } } @@ -594,25 +578,15 @@ void KeyframeViewBase::AutoSelectKeyTimeNeighbors() // Prevents infinite loop currently_autoselecting_ = true; - QList selected_items = scene()->selectedItems(); + QVector copy = GetSelectedKeyframes(); + foreach (NodeKeyframe *key, copy) { + rational key_time = key->time(); - foreach (QGraphicsItem* g, selected_items) { - KeyframeViewItem* key_item = static_cast(g); - - rational key_time = key_item->key()->time(); - - QVector keys = key_item->key()->parent()->GetKeyframesAtTime(key_item->key()->input(), key_time, key_item->key()->element()); + QVector keys = key->parent()->GetKeyframesAtTime(key->input(), key_time, key->element()); foreach (NodeKeyframe* k, keys) { - if (k == key_item->key()) { - continue; - } - - // Ensure this key is not already selected - KeyframeViewItem* item = item_map_.value(k); - - if (item) { - item->setSelected(true); + if (k != key) { + SelectKeyframe(k); } } } @@ -620,4 +594,9 @@ void KeyframeViewBase::AutoSelectKeyTimeNeighbors() currently_autoselecting_ = false; } +void KeyframeViewBase::Redraw() +{ + viewport()->update(); +} + } diff --git a/app/widget/keyframeview/keyframeviewbase.h b/app/widget/keyframeview/keyframeviewbase.h index 55b6ecf17..2ce0673f7 100644 --- a/app/widget/keyframeview/keyframeviewbase.h +++ b/app/widget/keyframeview/keyframeviewbase.h @@ -21,11 +21,12 @@ #ifndef KEYFRAMEVIEWBASE_H #define KEYFRAMEVIEWBASE_H -#include "keyframeviewitem.h" +#include "keyframeviewinputconnection.h" #include "node/keyframe.h" #include "widget/curvewidget/beziercontrolpointitem.h" #include "widget/menu/menu.h" #include "widget/timebased/timebasedview.h" +#include "widget/timebased/timebasedviewselectionmanager.h" #include "widget/timetarget/timetarget.h" namespace olive { @@ -36,51 +37,49 @@ class KeyframeViewBase : public TimeBasedView, public TimeTargetObject public: KeyframeViewBase(QWidget* parent = nullptr); - virtual void Clear(); - void DeleteSelected(); - void AddKeyframesOfNode(Node* n); + using ElementConnections = QVector; + using InputConnections = QVector; + using NodeConnections = QMap; - void AddKeyframesOfInput(Node *n, const QString &input); + NodeConnections AddKeyframesOfNode(Node* n); - void AddKeyframesOfElement(const NodeInput &input); + InputConnections AddKeyframesOfInput(Node *n, const QString &input); - void AddKeyframesOfTrack(const NodeKeyframeTrackReference &ref); + ElementConnections AddKeyframesOfElement(const NodeInput &input); - void RemoveKeyframesOfNode(Node* n); + KeyframeViewInputConnection *AddKeyframesOfTrack(const NodeKeyframeTrackReference &ref); - void RemoveKeyframesOfInput(Node *n, const QString &input); - - void RemoveKeyframesOfElement(const NodeInput &input); - - void RemoveKeyframesOfTrack(const NodeKeyframeTrackReference &ref); + void RemoveKeyframesOfTrack(KeyframeViewInputConnection *connection); void SelectAll(); void DeselectAll(); + void Clear(); + + const QVector &GetSelectedKeyframes() const + { + return selection_manager_.GetSelectedObjects(); + } + signals: void Dragged(int current_x, int current_y); -public slots: - virtual KeyframeViewItem* AddKeyframe(NodeKeyframe* key); - - void RemoveKeyframe(NodeKeyframe* key); - protected: virtual void mousePressEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; + virtual void drawForeground(QPainter *painter, const QRectF &rect) override; + virtual void ScaleChangedEvent(const double& scale) override; - const QMap& item_map() const; - - virtual void KeyframeAboutToBeRemoved(NodeKeyframe* key); - virtual void TimeTargetChangedEvent(Node*) override; + virtual void TimebaseChangedEvent(const rational &timebase) override; + virtual void ContextMenuEvent(Menu &m); bool IsDragging() const @@ -88,6 +87,19 @@ protected: return dragging_; } + void SelectKeyframe(NodeKeyframe *key); + + void DeselectKeyframe(NodeKeyframe *key); + + bool IsKeyframeSelected(NodeKeyframe *key) const + { + return selection_manager_.IsSelected(key); + } + + rational GetAdjustedKeyframeTime(NodeKeyframe *key); + + double GetKeyframeSceneX(NodeKeyframe *key); + private: rational CalculateNewTimeFromScreen(const rational& old_time, double cursor_diff); @@ -97,31 +109,20 @@ private: QPointF GetScaledCursorPos(const QPointF &cursor_pos); - struct KeyframeItemAndTime { - KeyframeViewItem* key; - qreal item_x; - rational time; - double value; - }; - - QMap item_map_; - - Tool::Item active_tool_; - QPointF drag_start_; BezierControlPointItem* dragging_bezier_point_; QPointF dragging_bezier_point_start_; QPointF dragging_bezier_point_opposing_start_; - KeyframeViewItem* initial_drag_item_; - - QVector selected_keys_; + QVector tracks_; bool currently_autoselecting_; bool dragging_; + TimeBasedViewSelectionManager selection_manager_; + private slots: void ShowContextMenu(); @@ -129,6 +130,8 @@ private slots: void AutoSelectKeyTimeNeighbors(); + void Redraw(); + }; } diff --git a/app/widget/keyframeview/keyframeviewinputconnection.cpp b/app/widget/keyframeview/keyframeviewinputconnection.cpp new file mode 100644 index 000000000..a8e9a35e5 --- /dev/null +++ b/app/widget/keyframeview/keyframeviewinputconnection.cpp @@ -0,0 +1,83 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "keyframeviewinputconnection.h" + +#include "keyframeview.h" + +namespace olive { + +KeyframeViewInputConnection::KeyframeViewInputConnection(const NodeKeyframeTrackReference &input, KeyframeViewBase *parent) : + QObject(parent), + keyframe_view_(parent), + input_(input), + y_(0), + y_behavior_(kSingleRow), + brush_(Qt::white) +{ + Node *n = input.input().node(); + + connect(n, &Node::KeyframeAdded, this, &KeyframeViewInputConnection::AddKeyframe); + connect(n, &Node::KeyframeRemoved, this, &KeyframeViewInputConnection::RemoveKeyframe); + connect(n, &Node::KeyframeTimeChanged, this, &KeyframeViewInputConnection::RequireUpdate); +} + +void KeyframeViewInputConnection::SetKeyframeY(int y) +{ + if (y_ != y) { + y_ = y; + + emit RequireUpdate(); + } +} + +void KeyframeViewInputConnection::SetYBehavior(YBehavior e) +{ + if (y_behavior_ != e) { + y_behavior_ = e; + + emit RequireUpdate(); + } +} + +void KeyframeViewInputConnection::SetBrush(const QBrush &brush) +{ + if (brush_ != brush) { + brush_ = brush; + + emit RequireUpdate(); + } +} + +void KeyframeViewInputConnection::AddKeyframe(NodeKeyframe *key) +{ + if (key->key_track_ref() == input_) { + emit RequireUpdate(); + } +} + +void KeyframeViewInputConnection::RemoveKeyframe(NodeKeyframe *key) +{ + if (key->key_track_ref() == input_) { + emit RequireUpdate(); + } +} + +} diff --git a/app/widget/keyframeview/keyframeviewinputconnection.h b/app/widget/keyframeview/keyframeviewinputconnection.h new file mode 100644 index 000000000..b8f951d22 --- /dev/null +++ b/app/widget/keyframeview/keyframeviewinputconnection.h @@ -0,0 +1,88 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef KEYFRAMEVIEWINPUTCONNECTION_H +#define KEYFRAMEVIEWINPUTCONNECTION_H + +#include + +#include "node/node.h" +#include "node/param.h" + +namespace olive { + +class KeyframeViewBase; + +class KeyframeViewInputConnection : public QObject +{ + Q_OBJECT +public: + KeyframeViewInputConnection(const NodeKeyframeTrackReference &input, KeyframeViewBase *parent); + + const int &GetKeyframeY() const + { + return y_; + } + + void SetKeyframeY(int y); + + enum YBehavior { + kSingleRow, + kValueIsHeight + }; + + void SetYBehavior(YBehavior e); + + const QVector GetKeyframes() const + { + return input_.input().node()->GetKeyframeTracks(input_.input()).at(input_.track()); + } + + const QBrush &GetBrush() const + { + return brush_; + } + + void SetBrush(const QBrush &brush); + +signals: + void RequireUpdate(); + +private: + KeyframeViewBase *keyframe_view_; + + NodeKeyframeTrackReference input_; + + int y_; + + YBehavior y_behavior_; + + QBrush brush_; + +private slots: + void AddKeyframe(NodeKeyframe *key); + + void RemoveKeyframe(NodeKeyframe *key); + +}; + +} + +#endif // KEYFRAMEVIEWINPUTCONNECTION_H diff --git a/app/widget/keyframeview/keyframeviewitem.cpp b/app/widget/keyframeview/keyframeviewitem.cpp deleted file mode 100644 index 45becc01b..000000000 --- a/app/widget/keyframeview/keyframeviewitem.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#include "keyframeviewitem.h" - -#include -#include -#include -#include - -#include "common/qtutils.h" - -namespace olive { - -KeyframeViewItem::KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent) : - QGraphicsRectItem(parent), - key_(key), - scale_(1.0), - vert_center_(0), - use_custom_brush_(false) -{ - setFlag(QGraphicsItem::ItemIsSelectable); - - connect(key, &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos); - connect(key, &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw); - - int keyframe_size = QtUtils::QFontMetricsWidth(qApp->fontMetrics(), "Oi"); - int half_sz = keyframe_size/2; - setRect(-half_sz, -half_sz, keyframe_size, keyframe_size); - - UpdatePos(); - - // Set default brush -} - -void KeyframeViewItem::SetOverrideY(qreal vertical_center) -{ - vert_center_ = vertical_center; - UpdatePos(); -} - -void KeyframeViewItem::SetScale(double scale) -{ - scale_ = scale; - UpdatePos(); -} - -void KeyframeViewItem::SetOverrideBrush(const QBrush &b) -{ - use_custom_brush_ = true; - setBrush(b); -} - -NodeKeyframe* KeyframeViewItem::key() const -{ - return key_; -} - -void KeyframeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) -{ - painter->setRenderHint(QPainter::Antialiasing); - - painter->setPen(Qt::black); - - if (option->state & QStyle::State_Selected) { - painter->setBrush(widget->palette().highlight()); - } else if (use_custom_brush_) { - painter->setBrush(brush()); - } else { - painter->setBrush(widget->palette().text()); - } - - switch (key_->type()) { - case NodeKeyframe::kLinear: - { - QPointF points[] = { - QPointF(rect().center().x(), rect().top()), - QPointF(rect().right(), rect().center().y()), - QPointF(rect().center().x(), rect().bottom()), - QPointF(rect().left(), rect().center().y()) - }; - - painter->drawPolygon(points, 4); - break; - } - case NodeKeyframe::kBezier: - painter->drawEllipse(rect()); - break; - case NodeKeyframe::kHold: - painter->drawRect(rect()); - break; - } -} - -void KeyframeViewItem::TimeTargetChangedEvent(Node *) -{ - UpdatePos(); -} - -void KeyframeViewItem::UpdatePos() -{ - rational adjusted = GetAdjustedTime(key_->parent(), GetTimeTarget(), key_->time(), false); - - setPos(adjusted.toDouble() * scale_, vert_center_); -} - -void KeyframeViewItem::Redraw() -{ - QGraphicsItem::update(); -} - -} diff --git a/app/widget/keyframeview/keyframeviewitem.h b/app/widget/keyframeview/keyframeviewitem.h deleted file mode 100644 index 3f3725dc6..000000000 --- a/app/widget/keyframeview/keyframeviewitem.h +++ /dev/null @@ -1,68 +0,0 @@ -/*** - - Olive - Non-Linear Video Editor - Copyright (C) 2021 Olive Team - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -***/ - -#ifndef KEYFRAMEVIEWITEM_H -#define KEYFRAMEVIEWITEM_H - -#include - -#include "node/keyframe.h" -#include "widget/timetarget/timetarget.h" - -namespace olive { - -class KeyframeViewItem : public QObject, public QGraphicsRectItem, public TimeTargetObject -{ - Q_OBJECT -public: - KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent = nullptr); - - void SetOverrideY(qreal vertical_center); - - void SetScale(double scale); - - void SetOverrideBrush(const QBrush& b); - - NodeKeyframe* key() const; - -protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - - virtual void TimeTargetChangedEvent(Node* ) override; - -private: - NodeKeyframe* key_; - - double scale_; - - qreal vert_center_; - - bool use_custom_brush_; - -private slots: - void UpdatePos(); - - void Redraw(); - -}; - -} - -#endif // KEYFRAMEVIEWITEM_H diff --git a/app/widget/nodeparamview/nodeparamview.cpp b/app/widget/nodeparamview/nodeparamview.cpp index 0a6f743e5..b2e219478 100644 --- a/app/widget/nodeparamview/nodeparamview.cpp +++ b/app/widget/nodeparamview/nodeparamview.cpp @@ -229,6 +229,10 @@ void NodeParamView::SetContexts(const QVector &contexts) ctx->setVisible(false); } + if (keyframe_view_) { + keyframe_view_->Clear(); + } + if (focused_node_) { focused_node_ = nullptr; emit FocusedNodeChanged(nullptr); @@ -264,6 +268,10 @@ void NodeParamView::SetContexts(const QVector &contexts) foreach (NodeParamViewContext *ctx, context_items_) { SortItemsInContext(ctx); } + + if (keyframe_view_) { + QueueKeyframePositionUpdate(); + } } void NodeParamView::resizeEvent(QResizeEvent *event) @@ -348,54 +356,13 @@ void NodeParamView::QueueKeyframePositionUpdate() QMetaObject::invokeMethod(this, &NodeParamView::UpdateElementY, Qt::QueuedConnection); } -void NodeParamView::SignalNodeOrder() -{ - /* - // Sort by item Y (apparently there's no way in Qt to get the order of dock widgets) - QVector nodes; - QVector item_ys; - - for (auto it=items_.cbegin(); it!=items_.cend(); it++) { - int item_y = it.value()->pos().y(); - - bool inserted = false; - - for (int i=0; i item_y) { - item_ys.insert(i, item_y); - nodes.insert(i, it.key()); - inserted = true; - break; - } - } - - if (!inserted) { - item_ys.append(item_y); - nodes.append(it.key()); - } - } - - emit NodeOrderChanged(nodes); - */ -} - void NodeParamView::AddNode(Node *n, NodeParamViewContext *context) { NodeParamViewItem* item = new NodeParamViewItem(n, create_checkboxes_, context); - if (keyframe_view_) { - connect(n, &Node::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); - connect(n, &Node::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); - } - connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::SetTimeAndSignal); connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode); - connect(item, &NodeParamViewItem::dockLocationChanged, this, &NodeParamView::QueueKeyframePositionUpdate); - connect(item, &NodeParamViewItem::dockLocationChanged, this, &NodeParamView::SignalNodeOrder); connect(item, &NodeParamViewItem::PinToggled, this, &NodeParamView::PinNode); - connect(item, &NodeParamViewItem::ArrayExpandedChanged, this, &NodeParamView::QueueKeyframePositionUpdate); - connect(item, &NodeParamViewItem::ExpandedChanged, this, &NodeParamView::QueueKeyframePositionUpdate); - connect(item, &NodeParamViewItem::Moved, this, &NodeParamView::QueueKeyframePositionUpdate); connect(item, &NodeParamViewItem::InputCheckedChanged, this, &NodeParamView::SetInputChecked); if (create_checkboxes_) { @@ -422,48 +389,15 @@ void NodeParamView::AddNode(Node *n, NodeParamViewContext *context) } if (keyframe_view_) { - keyframe_view_->AddKeyframesOfNode(n); + connect(item, &NodeParamViewItem::dockLocationChanged, this, &NodeParamView::QueueKeyframePositionUpdate); + connect(item, &NodeParamViewItem::ArrayExpandedChanged, this, &NodeParamView::QueueKeyframePositionUpdate); + connect(item, &NodeParamViewItem::ExpandedChanged, this, &NodeParamView::QueueKeyframePositionUpdate); + connect(item, &NodeParamViewItem::Moved, this, &NodeParamView::QueueKeyframePositionUpdate); + + item->SetKeyframeConnections(keyframe_view_->AddKeyframesOfNode(n)); } } -/*void NodeParamView::AddNode(Node *node, Node *context, NodeParamViewContext *ctx_item) -{ - int dist = GetDistanceBetweenNodes(context, node); - - if (dist == -1) { - dist = 0; - } - - ctx_item->GetDockArea()->insert -}*/ - -void NodeParamView::RemoveNode(Node *n) -{ - qDebug() << "STUB!"; - /*if (keyframe_view_) { - keyframe_view_->RemoveKeyframesOfNode(n); - - disconnect(n, &Node::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe); - disconnect(n, &Node::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe); - } - - delete items_.take(n); - - if (focused_node_ == n) { - // Try to find new node with gizmos to focus - focused_node_ = nullptr; - for (auto it=items_.cbegin(); it!=items_.cend(); it++) { - if (it.key()->HasGizmos()) { - focused_node_ = it.key(); - it.value()->SetHighlighted(true); - break; - } - } - - emit FocusedNodeChanged(focused_node_); - }*/ -} - int GetDistanceBetweenNodes(Node *start, Node *end) { if (start == end) { @@ -516,7 +450,7 @@ void NodeParamView::UpdateGlobalScrollBar() int height_offscreen = param_widget_container_->height() + scrollbar()->height(); if (keyframe_view_) { - keyframe_view_->SetMaxScroll(height_offscreen); + keyframe_view_->SetMaxScroll(height_offscreen + 2000); } vertical_scrollbar_->setRange(0, height_offscreen - param_scroll_area_->height()); @@ -533,8 +467,7 @@ void NodeParamView::PinNode(bool pin) pinned_nodes_.removeOne(node); if (!active_nodes_.contains(node)) { - RemoveNode(node); - SignalNodeOrder(); + //RemoveNode(node); } } } @@ -591,21 +524,34 @@ void NodeParamView::KeyframeViewDragged(int x, int y) void NodeParamView::UpdateElementY() { - qDebug() << "STUB"; - /*if (keyframe_view_) { - for (auto it=items_.cbegin(); it!=items_.cend(); it++) { - foreach (const QString& input, it.key()->inputs()) { - int arr_sz = it.key()->InputArraySize(input); + foreach (NodeParamViewContext *ctx, context_items_) { + for (auto it=ctx->GetItems().cbegin(); it!=ctx->GetItems().cend(); it++) { + const KeyframeViewBase::NodeConnections &connections = it.value()->GetKeyframeConnections(); - for (int i=-1; iinputs()) { + if (!(it.key()->GetInputFlags(input) & kInputFlagHidden)) { + int arr_sz = it.key()->InputArraySize(input); - int y = it.value()->GetElementY(ic); - keyframe_view_->SetElementY(ic, y); + for (int i=-1; iGetElementY(ic); + + const KeyframeViewBase::InputConnections &input_con = connections.value(input); + int use_index = i + 1; + if (use_index < input_con.size()) { + const KeyframeViewBase::ElementConnections &ele_con = input_con.at(ic.element()+1); + foreach (KeyframeViewInputConnection *track, ele_con) { + track->SetKeyframeY(y); + } + } + } + } } } } - }*/ + } } } diff --git a/app/widget/nodeparamview/nodeparamview.h b/app/widget/nodeparamview/nodeparamview.h index c1c8fb3b5..b7eb50edd 100644 --- a/app/widget/nodeparamview/nodeparamview.h +++ b/app/widget/nodeparamview/nodeparamview.h @@ -99,8 +99,6 @@ public slots: signals: void RequestSelectNode(const QVector& target); - void NodeOrderChanged(const QVector& nodes); - void FocusedNodeChanged(Node* n); protected: @@ -117,14 +115,8 @@ private: void QueueKeyframePositionUpdate(); - void SignalNodeOrder(); - void AddNode(Node* n, NodeParamViewContext *context); - //void AddNode(Node *node, Node *context, NodeParamViewContext *ctx_item); - - void RemoveNode(Node* n); - void SortItemsInContext(NodeParamViewContext *context); KeyframeView* keyframe_view_; diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 445bcace2..1e0aae242 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -36,6 +36,7 @@ #include "nodeparamviewwidgetbridge.h" #include "widget/clickablelabel/clickablelabel.h" #include "widget/collapsebutton/collapsebutton.h" +#include "widget/keyframeview/keyframeviewbase.h" namespace olive { @@ -187,6 +188,16 @@ public: void SetInputChecked(const NodeInput &input, bool e); + const KeyframeViewBase::NodeConnections &GetKeyframeConnections() const + { + return keyframe_connections_; + } + + void SetKeyframeConnections(const KeyframeViewBase::NodeConnections &c) + { + keyframe_connections_ = c; + } + signals: void RequestSetTime(const rational& time); @@ -206,6 +217,8 @@ private: rational time_; + KeyframeViewBase::NodeConnections keyframe_connections_; + }; } diff --git a/app/widget/timebased/CMakeLists.txt b/app/widget/timebased/CMakeLists.txt index e2a35686f..ed200fc08 100644 --- a/app/widget/timebased/CMakeLists.txt +++ b/app/widget/timebased/CMakeLists.txt @@ -18,6 +18,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/timebased/timebasedview.cpp widget/timebased/timebasedview.h + widget/timebased/timebasedviewselectionmanager.cpp + widget/timebased/timebasedviewselectionmanager.h widget/timebased/timebasedwidget.cpp widget/timebased/timebasedwidget.h widget/timebased/timescaledobject.cpp diff --git a/app/widget/timebased/timebasedviewselectionmanager.cpp b/app/widget/timebased/timebasedviewselectionmanager.cpp new file mode 100644 index 000000000..dd44a172e --- /dev/null +++ b/app/widget/timebased/timebasedviewselectionmanager.cpp @@ -0,0 +1,26 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "timebasedviewselectionmanager.h" + +namespace olive { + + +} diff --git a/app/widget/timebased/timebasedviewselectionmanager.h b/app/widget/timebased/timebasedviewselectionmanager.h new file mode 100644 index 000000000..d1f53d7d6 --- /dev/null +++ b/app/widget/timebased/timebasedviewselectionmanager.h @@ -0,0 +1,256 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef TIMEBASEDVIEWSELECTIONMANAGER_H +#define TIMEBASEDVIEWSELECTIONMANAGER_H + +#include +#include +#include + +#include "common/rational.h" +#include "common/timecodefunctions.h" +#include "timebasedview.h" + +namespace olive { + +template +class TimeBasedViewSelectionManager +{ +public: + TimeBasedViewSelectionManager(TimeBasedView *view) : + view_(view) + {} + + void ClearDrawnObjects() + { + drawn_objects_.clear(); + } + + void DeclareDrawnObject(T *object, const QRectF &pos) + { + drawn_objects_.append({object, pos}); + } + + bool Select(T *key) + { + if (!IsSelected(key)) { + selected_.append(key); + return true; + } + + return false; + } + + bool Deselect(T *key) + { + return selected_.removeOne(key); + } + + void ClearSelection() + { + selected_.clear(); + } + + bool IsSelected(T *key) const + { + return selected_.contains(key); + } + + const QVector &GetSelectedObjects() const + { + return selected_; + } + + void SetTimebase(const rational &tb) + { + timebase_ = tb; + } + + T *MousePress(QMouseEvent *event) + { + T *key_under_cursor = nullptr; + + if (event->button() == Qt::LeftButton) { + // See if there's a keyframe in this position + QPointF scene_pos = view_->mapToScene(event->pos()); + foreach (const DrawnObject &kp, drawn_objects_) { + if (kp.second.contains(scene_pos)) { + key_under_cursor = kp.first; + break; + } + } + + bool holding_shift = event->modifiers() & Qt::ShiftModifier; + + if (IsSelected(key_under_cursor)) { + if (holding_shift) { + // If selected and holding shift, de-select this item but do nothing else + Deselect(key_under_cursor); + } + } else { + if (!holding_shift) { + // If not already selecting and not holding shift, clear the current selection + ClearSelection(); + } + + // Add item to selection, either nothing if shift wasn't held, or the existing selection + Select(key_under_cursor); + } + } + + return key_under_cursor; + } + + bool IsDragging() const + { + return !dragging_.isEmpty(); + } + + void DragStart(T *initial_item, QMouseEvent *event) + { + initial_drag_item_ = initial_item; + + dragging_.clear(); + + dragging_.resize(selected_.size()); + for (int i=0; itime()}; + } + + drag_mouse_start_ = view_->mapToScene(event->pos()); + } + + void DragMove(QMouseEvent *event, const QString &tip_format) + { + QPointF diff = view_->mapToScene(event->pos()) - drag_mouse_start_; + + for (int i=0; iSceneToTimeNoGrid(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) { + 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) { + break; + } + + proposed_time += adj; + } + + sel->set_time(proposed_time); + } + + // Show information about this keyframe + QString tip = Timecode::time_to_timecode(initial_drag_item_->time(), timebase_, + Core::instance()->GetTimecodeDisplay(), false); + + if (!tip_format.isEmpty()) { + tip = tip_format.arg(tip); + } + + QToolTip::hideText(); + QToolTip::showText(QCursor::pos(), tip); + } + + void DragStop(MultiUndoCommand *command) + { + QToolTip::hideText(); + + for (int i=0; iadd_child(new SetTimeCommand(selected_.at(i), selected_.at(i)->time(), dragging_.at(i).time)); + } + } + +private: + class SetTimeCommand : public UndoCommand + { + public: + SetTimeCommand(T* key, const rational& time) + { + key_ = key; + new_time_ = time; + old_time_ = key_->time(); + } + + SetTimeCommand(T* key, const rational& new_time, const rational& old_time) + { + key_ = key; + new_time_ = new_time; + old_time_ = old_time; + } + + virtual Project* GetRelevantProject() const override + { + return key_->parent()->project(); + } + + protected: + virtual void redo() override + { + key_->set_time(new_time_); + } + + virtual void undo() override + { + key_->set_time(old_time_); + } + + private: + T* key_; + + rational old_time_; + rational new_time_; + + }; + + TimeBasedView *view_; + + using DrawnObject = QPair; + QVector drawn_objects_; + + QVector selected_; + + struct DragObject + { + rational time; + }; + + QVector dragging_; + + T *initial_drag_item_; + + QPointF drag_mouse_start_; + + rational timebase_; + +}; + +} + +#endif // TIMEBASEDVIEWSELECTIONMANAGER_H diff --git a/app/widget/timebased/timescaledobject.cpp b/app/widget/timebased/timescaledobject.cpp index 85b154a48..5ea4176aa 100644 --- a/app/widget/timebased/timescaledobject.cpp +++ b/app/widget/timebased/timescaledobject.cpp @@ -72,6 +72,13 @@ rational TimeScaledObject::SceneToTime(const double &x, const double &x_scale, c return rational(rounded_x_mvmt * timebase.numerator(), timebase.denominator()); } +rational TimeScaledObject::SceneToTimeNoGrid(const double &x, const double &x_scale) +{ + double unscaled_time = x / x_scale; + + return rational::fromDouble(unscaled_time); +} + double TimeScaledObject::TimeToScene(const rational &time) const { return time.toDouble() * scale_; @@ -82,6 +89,11 @@ rational TimeScaledObject::SceneToTime(const double &x, bool round) const return SceneToTime(x, scale_, timebase_, round); } +rational TimeScaledObject::SceneToTimeNoGrid(const double &x) const +{ + return SceneToTimeNoGrid(x, scale_); +} + void TimeScaledObject::SetMaximumScale(const double &max) { max_scale_ = max; diff --git a/app/widget/timebased/timescaledobject.h b/app/widget/timebased/timescaledobject.h index 8c858c8fc..acdc44acb 100644 --- a/app/widget/timebased/timescaledobject.h +++ b/app/widget/timebased/timescaledobject.h @@ -42,6 +42,7 @@ public: const double& timebase_dbl() const; static rational SceneToTime(const double &x, const double& x_scale, const rational& timebase, bool round = false); + static rational SceneToTimeNoGrid(const double &x, const double& x_scale); const double& GetScale() const; const double &GetMaximumScale() const { return max_scale_; } @@ -54,6 +55,7 @@ public: double TimeToScene(const rational& time) const; rational SceneToTime(const double &x, bool round = false) const; + rational SceneToTimeNoGrid(const double &x) const; protected: virtual void TimebaseChangedEvent(const rational&){} diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index c26b9bdf9..2cbf094d0 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -47,7 +47,7 @@ MainWindow::MainWindow(QWidget *parent) : #ifdef Q_OS_WINDOWS // Set up taskbar button progress bar (used for some modal tasks like exporting) - taskbar_btn_id_ = RegisterWindowMessage("TaskbarButtonCreated"); + taskbar_btn_id_ = RegisterWindowMessage(TEXT("TaskbarButtonCreated")); taskbar_interface_ = nullptr; #endif @@ -106,9 +106,6 @@ MainWindow::MainWindow(QWidget *parent) : connect(curve_panel_, &ParamPanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); connect(curve_panel_, &ParamPanel::TimeChanged, param_panel_, &NodeTablePanel::SetTime); - // Connect node order signals - connect(param_panel_, &ParamPanel::NodeOrderChanged, curve_panel_, &CurvePanel::SetNodes); - connect(PanelManager::instance(), &PanelManager::FocusedPanelChanged, this, &MainWindow::FocusedPanelChanged); sequence_viewer_panel_->ConnectTimeBasedPanel(param_panel_); @@ -116,7 +113,7 @@ MainWindow::MainWindow(QWidget *parent) : UpdateTitle(); - QMetaObject::invokeMethod(this, "SetDefaultLayout", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, &MainWindow::SetDefaultLayout, Qt::QueuedConnection); } MainWindow::~MainWindow()