From 7dd7e83373bf3b06a39125d5f824ac511142d72e Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Fri, 9 Aug 2019 22:25:16 +1000 Subject: [PATCH] work towards dropping clips into the timeline --- app/node/input/media/media.cpp | 7 + app/node/input/media/media.h | 2 + app/panel/timeline/timeline.cpp | 8 +- app/widget/timelineview/timelineview.cpp | 177 +++++++++++++++--- app/widget/timelineview/timelineview.h | 15 ++ .../timelineview/timelineviewghostitem.cpp | 51 ++++- .../timelineview/timelineviewghostitem.h | 20 +- app/widget/viewer/viewer.cpp | 1 + 8 files changed, 246 insertions(+), 35 deletions(-) diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 98f8be707..a31a134ca 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -72,6 +72,12 @@ NodeOutput *MediaInput::texture_output() return texture_output_; } +void MediaInput::SetFootage(Footage *f) +{ + // FIXME: Need some protection for Time == 0 + footage_input_->set_value(0, PtrToValue(f)); +} + void MediaInput::Process(const rational &time) { // FIXME: Use OCIO for color management @@ -96,6 +102,7 @@ void MediaInput::Process(const rational &time) } if (decoder_->stream() == nullptr) { + // FIXME: Hardcoded stream 0 decoder_->set_stream(footage->stream(0)); } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 089e299dc..40b3595ee 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -51,6 +51,8 @@ public: NodeOutput* texture_output(); + void SetFootage(Footage* f); + public slots: virtual void Process(const rational &time) override; diff --git a/app/panel/timeline/timeline.cpp b/app/panel/timeline/timeline.cpp index 9f5cd45e4..adfcfb21a 100644 --- a/app/panel/timeline/timeline.cpp +++ b/app/panel/timeline/timeline.cpp @@ -26,7 +26,6 @@ TimelinePanel::TimelinePanel(QWidget *parent) : PanelWidget(parent) { - // FIXME: Test code QWidget* main = new QWidget(this); setWidget(main); @@ -42,12 +41,13 @@ TimelinePanel::TimelinePanel(QWidget *parent) : connect(view_->horizontalScrollBar(), SIGNAL(valueChanged(int)), ruler_, SLOT(SetScroll(int))); connect(ruler_, SIGNAL(TimeChanged(const int64_t&)), view_, SLOT(SetTime(const int64_t&))); + + // FIXME: Test code + ruler_->SetScale(90.0); + view_->SetScale(90.0); // End test code Retranslate(); - -// ruler_->SetScale(90.0); -// view_->SetScale(90.0); } void TimelinePanel::Clear() diff --git a/app/widget/timelineview/timelineview.cpp b/app/widget/timelineview/timelineview.cpp index 2570f3c8c..57f94e045 100644 --- a/app/widget/timelineview/timelineview.cpp +++ b/app/widget/timelineview/timelineview.cpp @@ -20,13 +20,19 @@ #include "timelineview.h" +#include +#include #include #include #include +#include "node/input/media/media.h" +#include "project/item/footage/footage.h" + TimelineView::TimelineView(QWidget *parent) : QGraphicsView(parent), - playhead_(0) + playhead_(0), + dragging_(false) { setScene(&scene_); setAlignment(Qt::AlignLeft | Qt::AlignTop); @@ -72,6 +78,7 @@ void TimelineView::SetScale(const double &scale) void TimelineView::SetTimebase(const rational &timebase) { timebase_ = timebase; + timebase_dbl_ = timebase_.toDouble(); playhead_line_->SetTimebase(timebase_); } @@ -96,38 +103,55 @@ void TimelineView::SetTime(const int64_t time) void TimelineView::mousePressEvent(QMouseEvent *event) { QGraphicsView::mousePressEvent(event); - - if (itemAt(event->pos()) != nullptr) { - QList selected_items = scene_.selectedItems(); - - foreach (QGraphicsItem* item, selected_items) { - TimelineViewGhostItem* ghost = new TimelineViewGhostItem(); - TimelineViewClipItem* clip_item = static_cast(item); - - ClipBlock* clip = clip_item->clip(); - - ghost->SetIn(clip->in()); - ghost->SetOut(clip->out()); - ghost->SetScale(scale_); - - ghost->setPos(clip_item->pos()); - - ghost_items_.append(ghost); - - scene_.addItem(ghost); - } - } } void TimelineView::mouseMoveEvent(QMouseEvent *event) { QGraphicsView::mouseMoveEvent(event); - if (!ghost_items_.isEmpty()) { - foreach (TimelineViewGhostItem* ghost, ghost_items_) { - QPointF scene_pos = mapToScene(event->pos()); + if (!dragging_) { + // Let's see if there's anything selected to drag + if (itemAt(event->pos()) != nullptr) { + QList selected_items = scene_.selectedItems(); - ghost->setX(qMax(0.0, scene_pos.x())); + foreach (QGraphicsItem* item, selected_items) { + TimelineViewGhostItem* ghost = new TimelineViewGhostItem(); + TimelineViewClipItem* clip_item = static_cast(item); + + ClipBlock* clip = clip_item->clip(); + + ghost->SetIn(clip->in()); + ghost->SetOut(clip->out()); + ghost->SetScale(scale_); + + ghost->setPos(clip_item->pos()); + + ghost_items_.append(ghost); + + scene_.addItem(ghost); + } + + drag_start_ = event->pos(); + } + + dragging_ = true; + } else if (!ghost_items_.isEmpty()) { + QPoint movement = event->pos() - drag_start_; + + rational time_movement = ScreenToTime(movement.x()); + + // Validate movement + foreach (TimelineViewGhostItem* ghost, ghost_items_) { + rational validator = ghost->In() + time_movement; + if (validator < 0) { + time_movement = rational(0) - ghost->In(); + } + } + + // Perform movement + foreach (TimelineViewGhostItem* ghost, ghost_items_) { + ghost->SetInAdjustment(time_movement); + ghost->SetOutAdjustment(time_movement); } } } @@ -136,6 +160,107 @@ void TimelineView::mouseReleaseEvent(QMouseEvent *event) { QGraphicsView::mouseReleaseEvent(event); + ClearGhosts(); + + dragging_ = false; +} + +void TimelineView::dragEnterEvent(QDragEnterEvent *event) +{ + QStringList mime_formats = event->mimeData()->formats(); + + // Listen for MIME data from a ProjectViewModel + if (mime_formats.contains("application/x-oliveprojectitemdata")) { + // FIXME: Implement audio insertion + + // Data is drag/drop data from a ProjectViewModel + QByteArray model_data = event->mimeData()->data("application/x-oliveprojectitemdata"); + + // Use QDataStream to deserialize the data + QDataStream stream(&model_data, QIODevice::ReadOnly); + + // Variables to deserialize into + quintptr item_ptr; + int r; + + while (!stream.atEnd()) { + stream >> r >> item_ptr; + + // Get Item object + Item* item = reinterpret_cast(item_ptr); + + // Check if Item is Footage + if (item->type() == Item::kFootage) { + // If the Item is Footage, we can create a Ghost from it + Footage* footage = static_cast(item); + + StreamPtr stream = footage->stream(0); + + TimelineViewGhostItem* ghost = new TimelineViewGhostItem(); + + rational footage_duration(stream->timebase().numerator() * stream->duration(), + stream->timebase().denominator()); + + ghost->SetIn(0); + ghost->SetOut(footage_duration); + ghost->SetScale(scale_); + ghost->SetStream(stream); + + ghost_items_.append(ghost); + + scene_.addItem(ghost); + } + } + + event->accept(); + } else { + // FIXME: Implement dropping from file + event->ignore(); + } +} + +void TimelineView::dragMoveEvent(QDragMoveEvent *event) +{ + if (ghost_items_.isEmpty()) { + event->ignore(); + } else { + event->accept(); + } +} + +void TimelineView::dragLeaveEvent(QDragLeaveEvent *event) +{ + if (ghost_items_.isEmpty()) { + event->ignore(); + } else { + ClearGhosts(); + + event->accept(); + } +} + +void TimelineView::dropEvent(QDropEvent *event) +{ + if (ghost_items_.isEmpty()) { + event->ignore(); + } else { + ClearGhosts(); + + event->accept(); + } +} + +rational TimelineView::ScreenToTime(const int &x) +{ + // Adjust screen point by scale and timebase + int scaled_x_mvmt = qRound(x / scale_ / timebase_dbl_); + + // Return a time in the timebase + return rational(scaled_x_mvmt * timebase_.numerator(), timebase_.denominator()); +} + +void TimelineView::ClearGhosts() +{ if (!ghost_items_.isEmpty()) { foreach (TimelineViewGhostItem* ghost, ghost_items_) { delete ghost; diff --git a/app/widget/timelineview/timelineview.h b/app/widget/timelineview/timelineview.h index 47171df6f..615d78ae8 100644 --- a/app/widget/timelineview/timelineview.h +++ b/app/widget/timelineview/timelineview.h @@ -50,13 +50,24 @@ protected: virtual void mouseMoveEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override; + virtual void dragEnterEvent(QDragEnterEvent *event) override; + virtual void dragMoveEvent(QDragMoveEvent *event) override; + virtual void dragLeaveEvent(QDragLeaveEvent *event) override; + virtual void dropEvent(QDropEvent *event) override; + private: + rational ScreenToTime(const int& x); + + void ClearGhosts(); + QGraphicsScene scene_; double scale_; rational timebase_; + double timebase_dbl_; + int64_t playhead_; QVector clip_items_; @@ -64,6 +75,10 @@ private: QVector ghost_items_; TimelineViewPlayheadItem* playhead_line_; + + bool dragging_; + + QPoint drag_start_; }; #endif // TIMELINEVIEW_H diff --git a/app/widget/timelineview/timelineviewghostitem.cpp b/app/widget/timelineview/timelineviewghostitem.cpp index eff3a2879..ef57c99e4 100644 --- a/app/widget/timelineview/timelineviewghostitem.cpp +++ b/app/widget/timelineview/timelineviewghostitem.cpp @@ -23,12 +23,23 @@ #include TimelineViewGhostItem::TimelineViewGhostItem(QGraphicsItem *parent) : - TimelineViewRect(parent) + TimelineViewRect(parent), + stream_(nullptr) { setBrush(Qt::NoBrush); setPen(QPen(Qt::yellow, 2)); // FIXME: Make customizable via CSS } +const rational &TimelineViewGhostItem::In() +{ + return in_; +} + +const rational &TimelineViewGhostItem::Out() +{ + return out_; +} + void TimelineViewGhostItem::SetIn(const rational &in) { in_ = in; @@ -43,11 +54,45 @@ void TimelineViewGhostItem::SetOut(const rational &out) UpdateRect(); } +void TimelineViewGhostItem::SetInAdjustment(const rational &in_adj) +{ + in_adj_ = in_adj; + + UpdateRect(); +} + +void TimelineViewGhostItem::SetOutAdjustment(const rational &out_adj) +{ + out_adj_ = out_adj; + + UpdateRect(); +} + +rational TimelineViewGhostItem::GetAdjustedIn() +{ + return in_ + in_adj_; +} + +rational TimelineViewGhostItem::GetAdjustedOut() +{ + return out_ + out_adj_; +} + +StreamPtr TimelineViewGhostItem::stream() +{ + return stream_; +} + +void TimelineViewGhostItem::SetStream(StreamPtr f) +{ + stream_ = f; +} + void TimelineViewGhostItem::UpdateRect() { - rational length = out_ - in_; + rational length = GetAdjustedOut() - GetAdjustedIn(); setRect(0, 0, TimeToScreenCoord(length), 100); - setPos(TimeToScreenCoord(in_), 0); + setPos(TimeToScreenCoord(GetAdjustedIn()), 0); } diff --git a/app/widget/timelineview/timelineviewghostitem.h b/app/widget/timelineview/timelineviewghostitem.h index 61837f689..f4360f02f 100644 --- a/app/widget/timelineview/timelineviewghostitem.h +++ b/app/widget/timelineview/timelineviewghostitem.h @@ -21,6 +21,7 @@ #ifndef TIMELINEVIEWGHOSTITEM_H #define TIMELINEVIEWGHOSTITEM_H +#include "project/item/footage/footage.h" #include "timelineviewrect.h" class TimelineViewGhostItem : public TimelineViewRect @@ -28,17 +29,32 @@ class TimelineViewGhostItem : public TimelineViewRect public: TimelineViewGhostItem(QGraphicsItem* parent = nullptr); + const rational& In(); + const rational& Out(); + void SetIn(const rational& in); void SetOut(const rational& out); + void SetInAdjustment(const rational& in_adj); + void SetOutAdjustment(const rational& out_adj); + + rational GetAdjustedIn(); + rational GetAdjustedOut(); + + StreamPtr stream(); + void SetStream(StreamPtr f); + protected: virtual void UpdateRect() override; -// virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override; - private: rational in_; rational out_; + + rational in_adj_; + rational out_adj_; + + StreamPtr stream_; }; #endif // TIMELINEVIEWGHOSTITEM_H diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 791f21144..a634dde58 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -55,6 +55,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : // FIXME: Test code SetTimebase(rational(1001, 30000)); + ruler_->SetScale(48.0); // End test code }