diff --git a/app/core.cpp b/app/core.cpp index 24b2b92d0..d4ae9f28c 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -218,13 +218,9 @@ void Core::CreateNewFolder() } // FIXME: Test code -#include "node/block/clip/clip.h" -#include "node/block/gap/gap.h" -#include "node/input/media/media.h" #include "node/output/timeline/timeline.h" #include "node/output/track/track.h" #include "node/output/viewer/viewer.h" -#include "node/generator/solid/solid.h" #include "panel/panelmanager.h" #include "panel/node/node.h" #include "panel/viewer/viewer.h" @@ -272,42 +268,21 @@ void Core::CreateNewSequence() folder, new_sequence); - // FIXME: Test code - GapBlock* gb = new GapBlock(); - gb->set_length(1); - new_sequence->AddNode(gb); - - ClipBlock* cb = new ClipBlock(); - cb->set_length(2); - new_sequence->AddNode(cb); - - TrackOutput* to = new TrackOutput(); - new_sequence->AddNode(to); - - ClipBlock* cb2 = new ClipBlock(); - cb2->set_length(2); - new_sequence->AddNode(cb2); - - TrackOutput* to2 = new TrackOutput(); - new_sequence->AddNode(to2); - TimelineOutput* tb = new TimelineOutput(); new_sequence->AddNode(tb); ViewerOutput* vo = new ViewerOutput(); new_sequence->AddNode(vo); - Block::ConnectBlocks(gb, cb); - Block::ConnectBlocks(cb, to); - Block::ConnectBlocks(cb2, to2); - NodeParam::ConnectEdge(to2->track_output(), to->track_input()); + TrackOutput* to = new TrackOutput(); + new_sequence->AddNode(to); + NodeParam::ConnectEdge(to->texture_output(), vo->texture_input()); NodeParam::ConnectEdge(to->track_output(), tb->track_input()); vo->AttachViewer(olive::panel_focus_manager->MostRecentlyFocused()); tb->AttachTimeline(olive::panel_focus_manager->MostRecentlyFocused()); olive::panel_focus_manager->MostRecentlyFocused()->SetGraph(new_sequence.get()); - // End test code olive::undo_stack.push(aic); } diff --git a/app/node/output/timeline/timeline.cpp b/app/node/output/timeline/timeline.cpp index 1cf8a3a53..358deb8e2 100644 --- a/app/node/output/timeline/timeline.cpp +++ b/app/node/output/timeline/timeline.cpp @@ -59,10 +59,10 @@ QString TimelineOutput::Description() void TimelineOutput::AttachTimeline(TimelinePanel *timeline) { if (attached_timeline_ != nullptr) { - //TimelineView* view = attached_timeline_->view(); + TimelineView* view = attached_timeline_->view(); //disconnect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int))); - //disconnect(view, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational))); + disconnect(view, SIGNAL(RequestPlaceBlock(Block*, rational, int)), this, SLOT(PlaceBlock(Block*, rational, int))); // Remove existing UI objects from TimelinePanel attached_timeline_->Clear(); @@ -80,10 +80,10 @@ void TimelineOutput::AttachTimeline(TimelinePanel *timeline) track->GenerateBlockWidgets(); } - //TimelineView* view = attached_timeline_->view(); + TimelineView* view = attached_timeline_->view(); //connect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int))); - //connect(view, SIGNAL(RequestPlaceBlock(Block*, rational)), this, SLOT(PlaceBlock(Block*, rational))); + connect(view, SIGNAL(RequestPlaceBlock(Block*, rational, int)), this, SLOT(PlaceBlock(Block*, rational, int))); } } @@ -158,6 +158,18 @@ void TimelineOutput::DetachTrack(TrackOutput *track) } } +void TimelineOutput::AddTrack() +{ + TrackOutput* track = new TrackOutput(); + static_cast(parent())->AddNode(track); + + if (track_cache_.isEmpty()) { + NodeParam::ConnectEdge(track->track_output(), track_input()); + } else { + NodeParam::ConnectEdge(track->track_output(), track_cache_.last()->track_input()); + } +} + void TimelineOutput::TrackConnectionAdded(NodeEdgePtr edge) { if (edge->input() != track_input()) { @@ -189,9 +201,7 @@ void TimelineOutput::TrackConnectionRemoved(NodeEdgePtr edge) void TimelineOutput::TrackAddedBlock(Block *block) { if (attached_timeline_ != nullptr) { - qDebug() << "track index lmao:" << GetTrackIndex(static_cast(sender())); - - attached_timeline_->view()->AddBlock(block); + attached_timeline_->view()->AddBlock(block, GetTrackIndex(static_cast(sender()))); } } @@ -227,3 +237,12 @@ void TimelineOutput::TrackEdgeRemoved(NodeEdgePtr edge) DetachTrack(added_track); } } + +void TimelineOutput::PlaceBlock(Block *block, rational start, int track) +{ + while (track >= track_cache_.size()) { + AddTrack(); + } + + track_cache_.at(track)->PlaceBlock(block, start); +} diff --git a/app/node/output/timeline/timeline.h b/app/node/output/timeline/timeline.h index 8d54554b4..b6fca7db1 100644 --- a/app/node/output/timeline/timeline.h +++ b/app/node/output/timeline/timeline.h @@ -57,6 +57,8 @@ private: void DetachTrack(TrackOutput* track); + void AddTrack(); + TimelinePanel* attached_timeline_; NodeInput* track_input_; @@ -97,6 +99,11 @@ private slots: */ void TrackEdgeRemoved(NodeEdgePtr edge); + /** + * @brief Forwards a PlaceBlock signal to the requested track + */ + void PlaceBlock(Block* block, rational start, int track); + }; #endif // TIMELINEOUTPUT_H diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index ab802cb9c..43536fb2c 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -56,49 +56,6 @@ public: NodeOutput* track_output(); -signals: - /** - * @brief Signal emitted when a Block is added to this Track - */ - void BlockAdded(Block* block); - - /** - * @brief Signal emitted when a Block is removed from this Track - */ - void BlockRemoved(Block* block); - -public slots: - virtual void Process(const rational &time) override; - -private: - /** - * @brief Sets this Block as the only block in the Timeline (creating essentially a one clip sequence) - */ - void ConnectBlockInternal(Block* block); - - /** - * @brief Disconnects t - */ - void RemoveBlockInternal(); - - /** - * @brief Adds a Block to the parent graph so it can be connected to other Nodes - * - * Also runs through Node's dependencies (the Nodes whose outputs are connected to this Node's inputs) - */ - void AddBlockToGraph(Block* block); - - Block* attached_block(); - - QVector block_cache_; - - Block* current_block_; - - NodeInput* track_input_; - - NodeOutput* track_output_; - -private slots: /** * @brief Adds Block `block` at the very beginning of the Sequence before all other clips */ @@ -160,6 +117,51 @@ private slots: * length */ void SpliceBlock(Block* inner, Block* outer, rational inner_in); + +signals: + /** + * @brief Signal emitted when a Block is added to this Track + */ + void BlockAdded(Block* block); + + /** + * @brief Signal emitted when a Block is removed from this Track + */ + void BlockRemoved(Block* block); + +public slots: + virtual void Process(const rational &time) override; + +private: + /** + * @brief Sets this Block as the only block in the Timeline (creating essentially a one clip sequence) + */ + void ConnectBlockInternal(Block* block); + + /** + * @brief Disconnects t + */ + void RemoveBlockInternal(); + + /** + * @brief Adds a Block to the parent graph so it can be connected to other Nodes + * + * Also runs through Node's dependencies (the Nodes whose outputs are connected to this Node's inputs) + */ + void AddBlockToGraph(Block* block); + + Block* attached_block(); + + QVector block_cache_; + + Block* current_block_; + + NodeInput* track_input_; + + NodeOutput* track_output_; + +private slots: + }; #endif // TRACKOUTPUT_H diff --git a/app/widget/timelineview/timelineview.cpp b/app/widget/timelineview/timelineview.cpp index e2080d742..3decc7b1f 100644 --- a/app/widget/timelineview/timelineview.cpp +++ b/app/widget/timelineview/timelineview.cpp @@ -41,6 +41,8 @@ TimelineView::TimelineView(QWidget *parent) : setDragMode(RubberBandDrag); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + connect(&scene_, SIGNAL(changed(const QList&)), this, SLOT(UpdateSceneRect())); + // Create playhead line and ensure it's always on top playhead_line_ = new TimelineViewPlayheadItem(); playhead_line_->setZValue(100); @@ -51,7 +53,7 @@ TimelineView::TimelineView(QWidget *parent) : SetScale(1.0); } -void TimelineView::AddBlock(Block *block) +void TimelineView::AddBlock(Block *block, int track) { switch (block->type()) { case Block::kClip: @@ -61,7 +63,10 @@ void TimelineView::AddBlock(Block *block) // Set up clip with view parameters (clip item will automatically size its rect accordingly) clip_item->SetClip(clip); + clip_item->SetY(GetTrackY(track)); + clip_item->SetHeight(GetTrackHeight(track)); clip_item->SetScale(scale_); + clip_item->SetTrack(track); // Add to list of clip items that can be iterated through clip_items_.insert(clip, clip_item); @@ -119,12 +124,17 @@ void TimelineView::SetTimebase(const rational &timebase) void TimelineView::Clear() { - scene_.removeItem(playhead_line_); + QMapIterator iterator(clip_items_); + + while (iterator.hasNext()) { + iterator.next(); + + if (iterator.value() != nullptr) { + delete iterator.value(); + } + } - scene_.clear(); clip_items_.clear(); - - scene_.addItem(playhead_line_); } void TimelineView::SetTime(const int64_t time) @@ -173,11 +183,26 @@ void TimelineView::resizeEvent(QResizeEvent *event) { QGraphicsView::resizeEvent(event); - if (scene_.height() < height()) { - QRectF rect = scene_.sceneRect(); - rect.setHeight(height() - horizontalScrollBar()->height() - 2); - scene_.setSceneRect(rect); + UpdateSceneRect(); +} + +int TimelineView::GetTrackY(int track_index) +{ + int y = 0; + + for (int i=0;i heights); + + return track; +} + void TimelineView::ClearGhosts() { if (!ghost_items_.isEmpty()) { @@ -221,6 +259,30 @@ void TimelineView::BlockChanged() } } +void TimelineView::UpdateSceneRect() +{ + QRectF bounding_rect = scene_.itemsBoundingRect(); + + // Ensure the scene left and top are always 0 + bounding_rect.setTopLeft(QPointF(0, 0)); + + // Ensure the scene height is always AT LEAST the height of the view + int minimum_height = height() - horizontalScrollBar()->height() - 2; + if (bounding_rect.height() < minimum_height) { + bounding_rect.setHeight(minimum_height); + } + + // Ensure playhead is the correct height + playhead_line_->UpdateRect(); + + // If the scene is already this rect, do nothing + if (scene_.sceneRect() == bounding_rect) { + return; + } + + scene_.setSceneRect(bounding_rect); +} + TimelineView::Tool::Tool(TimelineView *parent) : dragging_(false), parent_(parent) @@ -247,3 +309,13 @@ TimelineView *TimelineView::Tool::parent() { return parent_; } + +QPointF TimelineView::Tool::GetScenePos(const QPoint &screen_pos) +{ + return parent()->mapToScene(screen_pos); +} + +QGraphicsItem *TimelineView::Tool::GetItemAtScenePos(const QPointF &scene_pos) +{ + return parent()->scene_.itemAt(scene_pos, parent()->transform()); +} diff --git a/app/widget/timelineview/timelineview.h b/app/widget/timelineview/timelineview.h index 1750ce2f6..a1ccf5d7d 100644 --- a/app/widget/timelineview/timelineview.h +++ b/app/widget/timelineview/timelineview.h @@ -43,7 +43,7 @@ class TimelineView : public QGraphicsView public: TimelineView(QWidget* parent); - void AddBlock(Block* block); + void AddBlock(Block* block, int track); void RemoveBlock(Block* block); @@ -59,11 +59,7 @@ public slots: void SetTime(const int64_t time); signals: - void RequestInsertBlockAtIndex(Block* block, int index); - - void RequestPlaceBlock(Block* block, rational start); - - void RequestInsertBlockAtTime(Block* block, rational time); + void RequestPlaceBlock(Block* block, rational start, int track); protected: virtual void mousePressEvent(QMouseEvent *event) override; @@ -97,12 +93,17 @@ private: TimelineView* parent(); protected: + QPointF GetScenePos(const QPoint& screen_pos); + + QGraphicsItem* GetItemAtScenePos(const QPointF& scene_pos); + bool dragging_; - QPoint drag_start_; + QPointF drag_start_; private: TimelineView* parent_; + }; class PointerTool : public Tool @@ -126,6 +127,9 @@ private: virtual void DragDrop(QDropEvent *event) override; }; + int GetTrackY(int track_index); + int GetTrackHeight(int track_index); + PointerTool pointer_tool_; ImportTool import_tool_; @@ -133,7 +137,8 @@ private: bool HasGhosts(); - rational ScreenToTime(const int& x); + rational SceneToTime(const double &x); + int SceneToTrack(const double &y); void ClearGhosts(); @@ -151,6 +156,8 @@ private: QVector ghost_items_; + QVector track_heights_; + TimelineViewPlayheadItem* playhead_line_; private slots: @@ -161,6 +168,11 @@ private slots: * derivatives. */ void BlockChanged(); + + /** + * @brief Slot called whenever the view resizes or the scene contents change to enforce minimum scene sizes + */ + void UpdateSceneRect(); }; #endif // TIMELINEVIEW_H diff --git a/app/widget/timelineview/timelineviewclipitem.cpp b/app/widget/timelineview/timelineviewclipitem.cpp index 211f8e405..edcf12e6f 100644 --- a/app/widget/timelineview/timelineviewclipitem.cpp +++ b/app/widget/timelineview/timelineviewclipitem.cpp @@ -55,7 +55,7 @@ void TimelineViewClipItem::UpdateRect() double item_left = TimeToScreenCoord(clip_->in()); double item_width = TimeToScreenCoord(clip_->length()); - setRect(0, 0, item_width - 1, 64); + setRect(0, y_, item_width - 1, height_ - 1); setPos(item_left, 0.0); } @@ -63,13 +63,13 @@ void TimelineViewClipItem::paint(QPainter *painter, const QStyleOptionGraphicsIt { Q_UNUSED(widget) - /*QLinearGradient grad; - grad.setStart(0, 0); - grad.setFinalStop(0, rect().height()); - grad.setColorAt(0.0, QColor(192, 192, 255)); + QLinearGradient grad; + grad.setStart(0, rect().top()); + grad.setFinalStop(0, rect().bottom()); + grad.setColorAt(0.0, QColor(160, 160, 240)); grad.setColorAt(1.0, QColor(128, 128, 192)); - painter->fillRect(rect(), grad);*/ - painter->fillRect(rect(), QColor(128, 128, 192)); + painter->fillRect(rect(), grad); +// painter->fillRect(rect(), QColor(128, 128, 192)); if (option->state & QStyle::State_Selected) { painter->fillRect(rect(), QColor(0, 0, 0, 64)); diff --git a/app/widget/timelineview/timelineviewghostitem.cpp b/app/widget/timelineview/timelineviewghostitem.cpp index 7b1d9c41e..8548be464 100644 --- a/app/widget/timelineview/timelineviewghostitem.cpp +++ b/app/widget/timelineview/timelineviewghostitem.cpp @@ -102,7 +102,7 @@ void TimelineViewGhostItem::UpdateRect() { rational length = GetAdjustedOut() - GetAdjustedIn(); - setRect(0, 0, TimeToScreenCoord(length), 64); + setRect(0, y_, TimeToScreenCoord(length), height_ - 1); setPos(TimeToScreenCoord(GetAdjustedIn()), 0); } diff --git a/app/widget/timelineview/timelineviewrect.cpp b/app/widget/timelineview/timelineviewrect.cpp index 90a22df8c..4082ccf5e 100644 --- a/app/widget/timelineview/timelineviewrect.cpp +++ b/app/widget/timelineview/timelineviewrect.cpp @@ -22,11 +22,47 @@ TimelineViewRect::TimelineViewRect(QGraphicsItem* parent) : QGraphicsRectItem(parent), - scale_(1.0) + scale_(1.0), + y_(0), + height_(0) { } +const int &TimelineViewRect::Y() +{ + return y_; +} + +void TimelineViewRect::SetY(const int &y) +{ + y_ = y; + + UpdateRect(); +} + +const int &TimelineViewRect::Height() +{ + return height_; +} + +void TimelineViewRect::SetHeight(const int &height) +{ + height_ = height; + + UpdateRect(); +} + +const int &TimelineViewRect::Track() +{ + return track_; +} + +void TimelineViewRect::SetTrack(const int &track) +{ + track_ = track; +} + void TimelineViewRect::SetScale(const double &scale) { scale_ = scale; diff --git a/app/widget/timelineview/timelineviewrect.h b/app/widget/timelineview/timelineviewrect.h index 2151eedac..01f8bac43 100644 --- a/app/widget/timelineview/timelineviewrect.h +++ b/app/widget/timelineview/timelineviewrect.h @@ -33,6 +33,15 @@ class TimelineViewRect : public QGraphicsRectItem public: TimelineViewRect(QGraphicsItem* parent = nullptr); + const int& Y(); + void SetY(const int& y); + + const int& Height(); + void SetHeight(const int& height); + + const int& Track(); + void SetTrack(const int& track); + void SetScale(const double& scale); virtual void UpdateRect() = 0; @@ -41,6 +50,12 @@ protected: double TimeToScreenCoord(const rational& time); double scale_; + + int y_; + + int height_; + + int track_; }; #endif // TIMELINEVIEWRECT_H diff --git a/app/widget/timelineview/tool/import.cpp b/app/widget/timelineview/tool/import.cpp index 9e6d6a8da..b4929b861 100644 --- a/app/widget/timelineview/tool/import.cpp +++ b/app/widget/timelineview/tool/import.cpp @@ -47,8 +47,11 @@ void TimelineView::ImportTool::DragEnter(QDragEnterEvent *event) quintptr item_ptr; int r; + // Set drag start position + drag_start_ = GetScenePos(event->pos()); + // Set ghosts to start where the cursor entered - rational ghost_start = parent()->ScreenToTime(event->pos().x()); + rational ghost_start = parent()->SceneToTime(drag_start_.x()); while (!stream.atEnd()) { stream >> r >> item_ptr; @@ -79,8 +82,6 @@ void TimelineView::ImportTool::DragEnter(QDragEnterEvent *event) } } - drag_start_ = event->pos(); - event->accept(); } else { // FIXME: Implement dropping from file @@ -93,12 +94,22 @@ void TimelineView::ImportTool::DragMove(QDragMoveEvent *event) if (parent()->HasGhosts()) { // Move ghosts to the mouse cursor foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { - QPoint movement = event->pos() - drag_start_; + QPointF pos = GetScenePos(event->pos()); + QPointF movement = pos - drag_start_; - rational time_movement = parent()->ScreenToTime(movement.x()); + rational time_movement = parent()->SceneToTime(movement.x()); ghost->SetInAdjustment(time_movement); ghost->SetOutAdjustment(time_movement); + + int ghost_track = parent()->SceneToTrack(pos.y()); + ghost->SetTrack(ghost_track); + + int ghost_y = parent()->GetTrackY(ghost_track); + int ghost_height = parent()->GetTrackHeight(ghost_track); + + ghost->SetY(ghost_y); + ghost->SetHeight(ghost_height); } event->accept(); @@ -130,8 +141,6 @@ void TimelineView::ImportTool::DragDrop(QDropEvent *event) MediaInput* media = new MediaInput(); // Set parents to node_memory_manager in case no TimelineOutput receives this signal - // FIXME: Moving nodes to shared_ptrs might be a better idea, except they all use the QObject system for hierarchy - // already... clip->setParent(&node_memory_manager); media->setParent(&node_memory_manager); @@ -141,9 +150,9 @@ void TimelineView::ImportTool::DragDrop(QDropEvent *event) NodeParam::ConnectEdge(media->texture_output(), clip->texture_input()); if (event->keyboardModifiers() & Qt::ControlModifier) { - emit parent()->RequestInsertBlockAtTime(clip, ghost->GetAdjustedIn()); + //emit parent()->RequestInsertBlockAtTime(clip, ghost->GetAdjustedIn()); } else { - emit parent()->RequestPlaceBlock(clip, ghost->GetAdjustedIn()); + emit parent()->RequestPlaceBlock(clip, ghost->GetAdjustedIn(), ghost->Track()); } } diff --git a/app/widget/timelineview/tool/pointer.cpp b/app/widget/timelineview/tool/pointer.cpp index c4e5816c0..04ca82013 100644 --- a/app/widget/timelineview/tool/pointer.cpp +++ b/app/widget/timelineview/tool/pointer.cpp @@ -52,8 +52,10 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event) if (!dragging_) { + drag_start_ = GetScenePos(event->pos()); + // Let's see if there's anything selected to drag - if (parent()->itemAt(event->pos()) != nullptr) { + if (GetItemAtScenePos(drag_start_) != nullptr) { QList selected_items = parent()->scene_.selectedItems(); foreach (QGraphicsItem* item, selected_items) { @@ -62,6 +64,8 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event) ClipBlock* clip = clip_item->clip(); + ghost->SetY(clip_item->Y()); + ghost->SetHeight(clip_item->Height()); ghost->SetIn(clip->in()); ghost->SetOut(clip->out()); ghost->SetScale(parent()->scale_); @@ -73,16 +77,16 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event) parent()->scene_.addItem(ghost); } - - drag_start_ = event->pos(); } dragging_ = true; } else if (!parent()->ghost_items_.isEmpty()) { - QPoint movement = event->pos() - drag_start_; + QPointF scene_pos = GetScenePos(event->pos()); - rational time_movement = parent()->ScreenToTime(movement.x()); + QPointF movement = scene_pos - drag_start_; + + rational time_movement = parent()->SceneToTime(movement.x()); // Validate movement foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) {