From f2d64728f99deef492904e2236b71c65c7455b26 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 17 May 2021 11:14:56 +1000 Subject: [PATCH] work towards timeline/nodeview interactivity --- app/panel/panelmanager.cpp | 14 +- app/panel/panelmanager.h | 19 ++- app/panel/timeline/timeline.cpp | 8 +- app/panel/timeline/timeline.h | 6 +- app/widget/nodeview/nodeview.cpp | 36 ++++- app/widget/nodeview/nodeviewedge.cpp | 139 ++++++++++--------- app/widget/nodeview/nodeviewedge.h | 6 + app/widget/nodeview/nodeviewitem.cpp | 47 ++++--- app/widget/nodeview/nodeviewitem.h | 7 + app/widget/nodeview/nodeviewscene.cpp | 10 +- app/widget/timelinewidget/timelinewidget.cpp | 13 +- app/widget/timelinewidget/timelinewidget.h | 6 +- app/widget/timelinewidget/tool/import.cpp | 6 +- app/window/mainwindow/mainwindow.cpp | 17 +++ app/window/mainwindow/mainwindow.h | 2 + 15 files changed, 204 insertions(+), 132 deletions(-) diff --git a/app/panel/panelmanager.cpp b/app/panel/panelmanager.cpp index ce06e7f9c..779c2be35 100644 --- a/app/panel/panelmanager.cpp +++ b/app/panel/panelmanager.cpp @@ -28,8 +28,7 @@ PanelManager* PanelManager::instance_ = nullptr; PanelManager::PanelManager(QObject *parent) : QObject(parent), - locked_(false), - last_focused_panel_(nullptr) + locked_(false) { } @@ -46,11 +45,11 @@ const QList &PanelManager::panels() return focus_history_; } -PanelWidget *PanelManager::CurrentlyFocused() const +PanelWidget *PanelManager::CurrentlyFocused(bool enable_hover) const { // If hover focus is enabled, find the currently hovered panel and return it (if no panel is hovered, resort to // default behavior) - if (Config::Current()["HoverFocus"].toBool()) { + if (enable_hover && Config::Current()[QStringLiteral("HoverFocus")].toBool()) { PanelWidget* hovered = CurrentlyHovered(); if (hovered != nullptr) { @@ -111,7 +110,7 @@ void PanelManager::FocusChanged(QWidget *old, QWidget *now) if (panel_cast_test) { - if (last_focused_panel_ != panel_cast_test) { + if (focus_history_.first() != panel_cast_test) { // If so, bump this to the top of the focus history int panel_index = focus_history_.indexOf(panel_cast_test); @@ -130,7 +129,6 @@ void PanelManager::FocusChanged(QWidget *old, QWidget *now) focus_history_.move(panel_index, 0); } - last_focused_panel_ = panel_cast_test; emit FocusedPanelChanged(panel_cast_test); } @@ -158,10 +156,6 @@ void PanelManager::PanelDestroyed() PanelWidget* panel = static_cast(sender()); focus_history_.removeOne(panel); - - if (last_focused_panel_ == panel) { - last_focused_panel_ = focus_history_.isEmpty() ? nullptr : focus_history_.first(); - } } } diff --git a/app/panel/panelmanager.h b/app/panel/panelmanager.h index 1f9c68dee..50dfefce6 100644 --- a/app/panel/panelmanager.h +++ b/app/panel/panelmanager.h @@ -65,14 +65,12 @@ public: /** * @brief Return the currently focused widget, or nullptr if nothing is focused * - * This result == CurrentlyFocused() if HoverFocus is true + * This result == CurrentlyFocused() if HoverFocus is true and panel is hovered */ - PanelWidget* CurrentlyFocused() const; + PanelWidget* CurrentlyFocused(bool enable_hover = true) const; /** * @brief Return the widget that the mouse is currently hovering over, or nullptr if nothing is hovered over - * - * This result == CurrentlyFocused() if HoverFocus is true */ PanelWidget* CurrentlyHovered() const; @@ -155,13 +153,6 @@ private: */ static PanelManager* instance_; - /** - * @brief The last panel that was focused - * - * Stored to prevent emitting FocusedPanelChanged() multiple times for the same panel - */ - PanelWidget* last_focused_panel_; - private slots: /** * @brief Processing if a panel gets deleted @@ -195,6 +186,12 @@ T *PanelManager::CreatePanel(QWidget *parent) // Connect destroy signal so we can remove it from focus history connect(panel, &PanelWidget::destroyed, this, &PanelManager::PanelDestroyed, Qt::DirectConnection); + if (focus_history_.size() == 1) { + // This is the first panel, focus it + panel->SetBorderVisible(true); + emit FocusedPanelChanged(panel); + } + return panel; } diff --git a/app/panel/timeline/timeline.cpp b/app/panel/timeline/timeline.cpp index 44b1f3fba..390de75cd 100644 --- a/app/panel/timeline/timeline.cpp +++ b/app/panel/timeline/timeline.cpp @@ -33,13 +33,7 @@ TimelinePanel::TimelinePanel(QWidget *parent) : Retranslate(); - connect(tw, &TimelineWidget::BlocksSelected, this, &TimelinePanel::BlocksSelected); - connect(tw, &TimelineWidget::BlocksDeselected, this, &TimelinePanel::BlocksDeselected); -} - -void TimelinePanel::Clear() -{ - static_cast(GetTimeBasedWidget())->Clear(); + connect(tw, &TimelineWidget::BlockSelectionChanged, this, &TimelinePanel::BlockSelectionChanged); } void TimelinePanel::SplitAtPlayhead() diff --git a/app/panel/timeline/timeline.h b/app/panel/timeline/timeline.h index 1b33ac9fb..5b853dce3 100644 --- a/app/panel/timeline/timeline.h +++ b/app/panel/timeline/timeline.h @@ -35,8 +35,6 @@ class TimelinePanel : public TimeBasedPanel public: TimelinePanel(QWidget* parent); - void Clear(); - void SplitAtPlayhead(); QByteArray SaveSplitterState() const; @@ -98,9 +96,7 @@ protected: virtual void Retranslate() override; signals: - void BlocksSelected(const QVector& selected_blocks); - - void BlocksDeselected(const QVector& deselected_blocks); + void BlockSelectionChanged(const QVector& selected_blocks); }; diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp index fcfe6736e..dc241b79b 100644 --- a/app/widget/nodeview/nodeview.cpp +++ b/app/widget/nodeview/nodeview.cpp @@ -126,6 +126,17 @@ void NodeView::SetGraph(NodeGraph *graph, const QVector &nodes) item->SetNodePosition(it.value()); } } + + for (auto it=scene_.item_map().cbegin(); it!=scene_.item_map().cend(); it++) { + Node *node = it.key(); + for (auto jt=node->input_connections().cbegin(); jt!=node->input_connections().cend(); jt++) { + const NodeOutput &output = jt->second; + if (scene_.item_map().contains(output.node())) { + // Create edge since both input and output exist + scene_.AddEdge(output, jt->first); + } + } + } } } @@ -913,23 +924,26 @@ void NodeView::AddNode(Node *node) void NodeView::RemoveNode(Node *node) { - if (filter_mode_ == kFilterShowAll) { - scene_.RemoveNode(node); - } + scene_.RemoveNode(node); } void NodeView::AddEdge(const NodeOutput &output, const NodeInput &input) { if (filter_mode_ == kFilterShowAll) { scene_.AddEdge(output, input); + } else if (filter_mode_ == kFilterShowSelective) { + Node *output_node = output.node(); + Node *input_node = input.node(); + + if (scene_.item_map().contains(output_node) && scene_.item_map().contains(input_node)) { + scene_.AddEdge(output, input); + } } } void NodeView::RemoveEdge(const NodeOutput &output, const NodeInput &input) { - if (filter_mode_ == kFilterShowAll) { - scene_.RemoveEdge(output, input); - } + scene_.RemoveEdge(output, input); } void NodeView::AddNodePosition(Node *node, void *relative, const QPointF &pos) @@ -940,6 +954,16 @@ void NodeView::AddNodePosition(Node *node, void *relative, const QPointF &pos) if (!item) { item = scene_.AddNode(node); + + // Add input edges + for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { + AddEdge(it->second, it->first); + } + + // Add output edges + for (auto it=node->output_connections().cbegin(); it!=node->output_connections().cend(); it++) { + AddEdge(it->first, it->second); + } } item->SetNodePosition(pos); diff --git a/app/widget/nodeview/nodeviewedge.cpp b/app/widget/nodeview/nodeviewedge.cpp index 46357aa4e..e15f6125e 100644 --- a/app/widget/nodeview/nodeviewedge.cpp +++ b/app/widget/nodeview/nodeviewedge.cpp @@ -80,6 +80,82 @@ void NodeViewEdge::SetHighlighted(bool e) void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end, bool input_is_expanded) { + cached_start_ = start; + cached_end_ = end; + cached_input_is_expanded_ = input_is_expanded; + + UpdateCurve(); +} + +void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir) +{ + flow_dir_ = dir; + + if (from_item_ && to_item_) { + Adjust(); + } +} + +void NodeViewEdge::SetCurved(bool e) +{ + curved_ = e; + + UpdateCurve(); +} + +void NodeViewEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) +{ + QPalette::ColorGroup group; + QPalette::ColorRole role; + + if (connected_) { + group = QPalette::Active; + } else { + group = QPalette::Disabled; + } + + if (highlighted_ != bool(option->state & QStyle::State_Selected)) { + role = QPalette::Highlight; + } else { + role = QPalette::Text; + } + + // Draw main path + QColor edge_color = qApp->palette().color(group, role); + + painter->setPen(QPen(edge_color, edge_width_)); + painter->setBrush(Qt::NoBrush); + painter->drawPath(path()); + + // Draw arrow + painter->setPen(Qt::NoPen); + painter->setBrush(edge_color); + painter->drawPolygon(arrow_); +} + +void NodeViewEdge::Init() +{ + connected_ = false; + highlighted_ = false; + flow_dir_ = NodeViewCommon::kLeftToRight; + curved_ = true; + + setFlag(QGraphicsItem::ItemIsSelectable); + + // Ensures this UI object is drawn behind other objects + setZValue(-1); + + // Use font metrics to set edge width for basic high DPI support + edge_width_ = QFontMetrics(QFont()).height() / 12; + arrow_size_ = QFontMetrics(QFont()).height() / 2; +} + +void NodeViewEdge::UpdateCurve() +{ + const QPointF &start = cached_start_; + const QPointF &end = cached_end_; + const bool input_is_expanded = cached_input_is_expanded_; + QPainterPath path; path.moveTo(start); @@ -156,67 +232,4 @@ void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end, bool inpu arrow_bounding_rect_.adjust(-arrow_size_, -arrow_size_, arrow_size_, arrow_size_); } -void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir) -{ - flow_dir_ = dir; - - if (from_item_ && to_item_) { - Adjust(); - } -} - -void NodeViewEdge::SetCurved(bool e) -{ - curved_ = e; - - update(); -} - -void NodeViewEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) -{ - QPalette::ColorGroup group; - QPalette::ColorRole role; - - if (connected_) { - group = QPalette::Active; - } else { - group = QPalette::Disabled; - } - - if (highlighted_ != bool(option->state & QStyle::State_Selected)) { - role = QPalette::Highlight; - } else { - role = QPalette::Text; - } - - // Draw main path - QColor edge_color = qApp->palette().color(group, role); - - painter->setPen(QPen(edge_color, edge_width_)); - painter->setBrush(Qt::NoBrush); - painter->drawPath(path()); - - // Draw arrow - painter->setPen(Qt::NoPen); - painter->setBrush(edge_color); - painter->drawPolygon(arrow_); -} - -void NodeViewEdge::Init() -{ - connected_ = false; - highlighted_ = false; - flow_dir_ = NodeViewCommon::kLeftToRight; - curved_ = true; - - setFlag(QGraphicsItem::ItemIsSelectable); - - // Ensures this UI object is drawn behind other objects - setZValue(-1); - - // Use font metrics to set edge width for basic high DPI support - edge_width_ = QFontMetrics(QFont()).height() / 12; - arrow_size_ = QFontMetrics(QFont()).height() / 2; -} - } diff --git a/app/widget/nodeview/nodeviewedge.h b/app/widget/nodeview/nodeviewedge.h index cecdf6ccd..0f90108dc 100644 --- a/app/widget/nodeview/nodeviewedge.h +++ b/app/widget/nodeview/nodeviewedge.h @@ -122,6 +122,8 @@ protected: private: void Init(); + void UpdateCurve(); + NodeOutput output_; NodeInput input_; @@ -148,6 +150,10 @@ private: QRectF arrow_bounding_rect_; + QPointF cached_start_; + QPointF cached_end_; + bool cached_input_is_expanded_; + }; } diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp index d07fbaa9d..7e43e1f6b 100644 --- a/app/widget/nodeview/nodeviewitem.cpp +++ b/app/widget/nodeview/nodeviewitem.cpp @@ -97,24 +97,9 @@ QPointF NodeViewItem::GetNodePosition() const void NodeViewItem::SetNodePosition(const QPointF &pos) { - switch (flow_dir_) { - case NodeViewCommon::kLeftToRight: - setPos(pos.x() * DefaultItemHorizontalPadding(), - pos.y() * DefaultItemVerticalPadding()); - break; - case NodeViewCommon::kRightToLeft: - setPos(-pos.x() * DefaultItemHorizontalPadding(), - pos.y() * DefaultItemVerticalPadding()); - break; - case NodeViewCommon::kTopToBottom: - setPos(pos.y() * DefaultItemHorizontalPadding(), - pos.x() * DefaultItemVerticalPadding()); - break; - case NodeViewCommon::kBottomToTop: - setPos(pos.y() * DefaultItemHorizontalPadding(), - -pos.x() * DefaultItemVerticalPadding()); - break; - } + cached_node_pos_ = pos; + + UpdateNodePosition(); } int NodeViewItem::DefaultTextPadding() @@ -466,6 +451,8 @@ QPointF NodeViewItem::GetOutputPoint(const QString& output) const void NodeViewItem::SetFlowDirection(NodeViewCommon::FlowDirection dir) { flow_dir_ = dir; + + UpdateNodePosition(); } QPointF NodeViewItem::GetInputPointInternal(int index, const QPointF& source_pos) const @@ -490,4 +477,28 @@ QPointF NodeViewItem::GetInputPointInternal(int index, const QPointF& source_pos } } +void NodeViewItem::UpdateNodePosition() +{ + const QPointF &pos = cached_node_pos_; + + switch (flow_dir_) { + case NodeViewCommon::kLeftToRight: + setPos(pos.x() * DefaultItemHorizontalPadding(), + pos.y() * DefaultItemVerticalPadding()); + break; + case NodeViewCommon::kRightToLeft: + setPos(-pos.x() * DefaultItemHorizontalPadding(), + pos.y() * DefaultItemVerticalPadding()); + break; + case NodeViewCommon::kTopToBottom: + setPos(pos.y() * DefaultItemHorizontalPadding(), + pos.x() * DefaultItemVerticalPadding()); + break; + case NodeViewCommon::kBottomToTop: + setPos(pos.y() * DefaultItemHorizontalPadding(), + -pos.x() * DefaultItemVerticalPadding()); + break; + } +} + } diff --git a/app/widget/nodeview/nodeviewitem.h b/app/widget/nodeview/nodeviewitem.h index 07703760f..1edab3aa5 100644 --- a/app/widget/nodeview/nodeviewitem.h +++ b/app/widget/nodeview/nodeviewitem.h @@ -136,6 +136,11 @@ private: */ QPointF GetInputPointInternal(int index, const QPointF &source_pos) const; + /** + * @brief Internal update function when logical position changes + */ + void UpdateNodePosition(); + /** * @brief Reference to attached Node */ @@ -167,6 +172,8 @@ private: QVector edges_; + QPointF cached_node_pos_; + }; } diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp index e389b0ce4..4853c0e99 100644 --- a/app/widget/nodeview/nodeviewscene.cpp +++ b/app/widget/nodeview/nodeviewscene.cpp @@ -179,10 +179,12 @@ void NodeViewScene::AddEdge(const NodeOutput &output, const NodeInput &input) void NodeViewScene::RemoveEdge(const NodeOutput &output, const NodeInput &input) { NodeViewEdge* edge = EdgeToUIObject(output, input); - edge->from_item()->RemoveEdge(edge); - edge->to_item()->RemoveEdge(edge); - edges_.removeOne(edge); - delete edge; + if (edge) { + edge->from_item()->RemoveEdge(edge); + edge->to_item()->RemoveEdge(edge); + edges_.removeOne(edge); + delete edge; + } } int NodeViewScene::DetermineWeight(Node *n) diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index ddbed335b..c426f6893 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -871,7 +871,7 @@ void TimelineWidget::RemoveBlock(Block *block) selected_blocks_.removeAt(select_index); RemoveSelection(block); - emit BlocksDeselected({block}); + SignalBlockSelectionChange(); } } @@ -1087,6 +1087,11 @@ void TimelineWidget::SetScrollZoomsByDefaultOnAllViews(bool e) } } +void TimelineWidget::SignalBlockSelectionChange() +{ + emit BlockSelectionChanged(selected_blocks_); +} + void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost) { ghost_items_.append(ghost); @@ -1168,7 +1173,7 @@ void TimelineWidget::SignalSelectedBlocks(QVector input, bool filter) selected_blocks_.append(input); - emit BlocksSelected(input); + emit SignalBlockSelectionChange(); } void TimelineWidget::SignalDeselectedBlocks(const QVector &deselected_blocks) @@ -1181,14 +1186,14 @@ void TimelineWidget::SignalDeselectedBlocks(const QVector &deselected_b selected_blocks_.removeOne(b); } - emit BlocksDeselected(deselected_blocks); + emit SignalBlockSelectionChange(); } void TimelineWidget::SignalDeselectedAllBlocks() { if (!selected_blocks_.isEmpty()) { - emit BlocksDeselected(selected_blocks_); selected_blocks_.clear(); + SignalBlockSelectionChange(); } } diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index 55cf634eb..6b5ddc7d8 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -234,9 +234,7 @@ public: }; signals: - void BlocksSelected(const QVector& selected_blocks); - - void BlocksDeselected(const QVector& deselected_blocks); + void BlockSelectionChanged(const QVector& selected_blocks); protected: virtual void resizeEvent(QResizeEvent *event) override; @@ -355,6 +353,8 @@ private slots: void SetScrollZoomsByDefaultOnAllViews(bool e); + void SignalBlockSelectionChange(); + }; } diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 251e21e14..5f086fb74 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -387,8 +387,12 @@ void ImportTool::DropGhosts(bool insert) clip->set_length_and_media_out(ghost->GetLength()); clip->SetLabel(footage_stream.footage->GetLabel()); command->add_child(new NodeAddCommand(dst_graph, clip)); - command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(clip, footage_stream.footage, clip, QPointF(2, 0))); + command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(clip, footage_stream.footage, nullptr, QPointF(2, 0))); + // Position clip in its own context + command->add_child(new NodeSetPositionCommand(clip, clip, QPointF(0, 0), false)); + + // Position footage in its context command->add_child(new NodeSetPositionCommand(footage_stream.footage, clip, QPointF(-2, 0), false)); switch (Track::Reference::TypeFromString(footage_stream.output)) { diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 0329bd741..f5df542b7 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -466,6 +466,22 @@ void MainWindow::StatusBarDoubleClicked() task_man_panel_->raise(); } +void MainWindow::TimelinePanelSelectionChanged(const QVector &blocks) +{ + TimelinePanel *panel = static_cast(sender()); + + if (PanelManager::instance()->CurrentlyFocused(false) == panel) { + QVector context(blocks.size()); + for (int i=0; iGetConnectedViewer(); + + node_panel_->SetGraph(viewer ? viewer->parent() : nullptr, context); + } +} + #ifdef Q_OS_LINUX void MainWindow::ShowNouveauWarning() { @@ -539,6 +555,7 @@ TimelinePanel* MainWindow::AppendTimelinePanel() connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTimestamp); connect(panel, &TimelinePanel::TimeChanged, table_panel_, &NodeTablePanel::SetTimestamp); connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTimestamp); + connect(panel, &TimelinePanel::BlockSelectionChanged, this, &MainWindow::TimelinePanelSelectionChanged); connect(param_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); connect(curve_panel_, &ParamPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); connect(sequence_viewer_panel_, &SequenceViewerPanel::TimeChanged, panel, &TimelinePanel::SetTimestamp); diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 18c54f401..c9cf8e017 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -190,6 +190,8 @@ private slots: void ShowNouveauWarning(); #endif + void TimelinePanelSelectionChanged(const QVector &blocks); + }; }