From 9c650883c28e9f4e4044e2d15cf3d02649216346 Mon Sep 17 00:00:00 2001
From: itsmattkc <34096995+itsmattkc@users.noreply.github.com>
Date: Wed, 10 Nov 2021 16:33:59 -0800
Subject: [PATCH] implement context item in node view
---
app/node/CMakeLists.txt | 2 +
app/node/group.cpp | 30 +++++++
app/node/group.h | 43 +++++++++
app/node/output/track/track.h | 45 ++++++++--
app/widget/nodeview/CMakeLists.txt | 2 +
app/widget/nodeview/nodeview.cpp | 43 ++++++++-
app/widget/nodeview/nodeview.h | 5 ++
app/widget/nodeview/nodeviewcontext.cpp | 114 ++++++++++++++++++++++++
app/widget/nodeview/nodeviewcontext.h | 39 ++++++++
app/widget/nodeview/nodeviewitem.cpp | 4 +
app/widget/nodeview/nodeviewscene.cpp | 25 ++++++
app/widget/nodeview/nodeviewscene.h | 6 ++
12 files changed, 347 insertions(+), 11 deletions(-)
create mode 100644 app/node/group.cpp
create mode 100644 app/node/group.h
create mode 100644 app/widget/nodeview/nodeviewcontext.cpp
create mode 100644 app/widget/nodeview/nodeviewcontext.h
diff --git a/app/node/CMakeLists.txt b/app/node/CMakeLists.txt
index d10d5746f..6cd34ca5b 100644
--- a/app/node/CMakeLists.txt
+++ b/app/node/CMakeLists.txt
@@ -34,6 +34,8 @@ set(OLIVE_SOURCES
node/globals.h
node/graph.cpp
node/graph.h
+ node/group.cpp
+ node/group.h
node/hashtraverser.cpp
node/hashtraverser.h
node/inputdragger.cpp
diff --git a/app/node/group.cpp b/app/node/group.cpp
new file mode 100644
index 000000000..7b7ab0c87
--- /dev/null
+++ b/app/node/group.cpp
@@ -0,0 +1,30 @@
+/***
+
+ 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 "group.h"
+
+namespace olive {
+
+NodeGroup::NodeGroup()
+{
+
+}
+
+}
diff --git a/app/node/group.h b/app/node/group.h
new file mode 100644
index 000000000..2f15bbb71
--- /dev/null
+++ b/app/node/group.h
@@ -0,0 +1,43 @@
+/***
+
+ 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 NODEGROUP_H
+#define NODEGROUP_H
+
+#include "node.h"
+
+namespace olive {
+
+class NodeGroup : public Node
+{
+ Q_OBJECT
+public:
+ NodeGroup();
+
+ void SetNodes(Node *nodes);
+
+private:
+ QVector nodes_;
+
+};
+
+}
+
+#endif // NODEGROUP_H
diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h
index 97bb1e7b6..d6179eaf4 100644
--- a/app/node/output/track/track.h
+++ b/app/node/output/track/track.h
@@ -168,17 +168,46 @@ public:
QString ToString() const
{
- QString type_string;
-
- if (type_ == Track::kVideo) {
- type_string = QStringLiteral("v");
- } else if (type_ == Track::kAudio) {
- type_string = QStringLiteral("a");
- } else {
+ QString type_string = TypeToString(type_);
+ if (type_string.isEmpty()) {
return QString();
+ } else {
+ return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
+ }
+ }
+
+ /// For IDs that shouldn't change between localizations
+ static QString TypeToString(Type type)
+ {
+ switch (type) {
+ case kVideo:
+ return QStringLiteral("v");
+ case kAudio:
+ return QStringLiteral("a");
+ case kSubtitle:
+ return QStringLiteral("s");
+ case kCount:
+ break;
}
- return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
+ return QString();
+ }
+
+ /// For human-facing strings
+ static QString TypeToTranslatedString(Type type)
+ {
+ switch (type) {
+ case kVideo:
+ return tr("V");
+ case kAudio:
+ return tr("A");
+ case kSubtitle:
+ return tr("S");
+ case kCount:
+ break;
+ }
+
+ return QString();
}
static Type TypeFromString(const QString& s)
diff --git a/app/widget/nodeview/CMakeLists.txt b/app/widget/nodeview/CMakeLists.txt
index e73798ae5..e7f94c762 100644
--- a/app/widget/nodeview/CMakeLists.txt
+++ b/app/widget/nodeview/CMakeLists.txt
@@ -19,6 +19,8 @@ set(OLIVE_SOURCES
widget/nodeview/nodeview.cpp
widget/nodeview/nodeview.h
widget/nodeview/nodeviewcommon.h
+ widget/nodeview/nodeviewcontext.cpp
+ widget/nodeview/nodeviewcontext.h
widget/nodeview/nodeviewedge.cpp
widget/nodeview/nodeviewedge.h
widget/nodeview/nodeviewitem.cpp
diff --git a/app/widget/nodeview/nodeview.cpp b/app/widget/nodeview/nodeview.cpp
index a375ede4f..b694992b3 100644
--- a/app/widget/nodeview/nodeview.cpp
+++ b/app/widget/nodeview/nodeview.cpp
@@ -29,6 +29,7 @@
#include "node/audio/volume/volume.h"
#include "node/distort/transform/transformdistortnode.h"
#include "node/factory.h"
+#include "node/group.h"
#include "node/traverser.h"
#include "widget/menu/menushared.h"
#include "widget/timebased/timebasedview.h"
@@ -62,7 +63,7 @@ NodeView::NodeView(QWidget *parent) :
ConnectSelectionChangedSignal();
- SetFlowDirection(NodeViewCommon::kTopToBottom);
+ SetFlowDirection(NodeViewCommon::kLeftToRight);
UpdateSceneBoundingRect();
connect(&scene_, &QGraphicsScene::changed, this, &NodeView::UpdateSceneBoundingRect);
@@ -85,7 +86,23 @@ NodeView::~NodeView()
void NodeView::SetGraph(NodeGraph *graph, const QVector &nodes)
{
- bool graph_changed = graph_ != graph;
+ // Remove contexts that are no longer in the list
+ foreach (Node *n, filter_nodes_) {
+ if (!nodes.contains(n)) {
+ scene_.RemoveContext(n);
+ }
+ }
+
+ // Add contexts that are now in the list
+ foreach (Node *n, nodes) {
+ if (!filter_nodes_.contains(n)) {
+ scene_.AddContext(n);
+ }
+ }
+
+ filter_nodes_ = nodes;
+
+ /*bool graph_changed = graph_ != graph;
bool context_changed = last_set_filter_nodes_ != nodes;
if (graph_changed || context_changed) {
@@ -144,7 +161,7 @@ void NodeView::SetGraph(NodeGraph *graph, const QVector &nodes)
// Center on something
QMetaObject::invokeMethod(this, &NodeView::CenterOnItemsBoundingRect, Qt::QueuedConnection);
}
- }
+ }*/
}
void NodeView::ClearGraph()
@@ -850,6 +867,15 @@ void NodeView::ShowContextMenu(const QPoint &pos)
Core::instance()->LabelNodes(scene_.GetSelectedNodes());
});
+ // Grouping
+ if (selected.size() == 1 && dynamic_cast(selected.first()->GetNode())) {
+ QAction *ungroup_action = m.addAction(tr("Ungroup"));
+ connect(ungroup_action, &QAction::triggered, this, &NodeView::UngroupNodes);
+ } else {
+ QAction *group_action = m.addAction(tr("Group"));
+ connect(group_action, &QAction::triggered, this, &NodeView::GroupNodes);
+ }
+
// Color menu
MenuShared::instance()->AddColorCodingMenu(&m);
@@ -1603,6 +1629,17 @@ void NodeView::RepositionContexts()
}
}
+void NodeView::GroupNodes()
+{
+ /*NodeGroup *group = new NodeGroup();
+ selected_nodes_*/
+}
+
+void NodeView::UngroupNodes()
+{
+ static_cast(selected_nodes_.first());
+}
+
NodeViewItem *NodeView::UpdateNodeItem(Node *node, bool ignore_own_context)
{
// Get UI item or create if it doesn't exist
diff --git a/app/widget/nodeview/nodeview.h b/app/widget/nodeview/nodeview.h
index db03afc08..941120361 100644
--- a/app/widget/nodeview/nodeview.h
+++ b/app/widget/nodeview/nodeview.h
@@ -27,6 +27,7 @@
#include "node/graph.h"
#include "node/nodecopypaste.h"
#include "nodeviewedge.h"
+#include "nodeviewcontext.h"
#include "nodeviewminimap.h"
#include "nodeviewscene.h"
#include "widget/handmovableview/handmovableview.h"
@@ -303,6 +304,10 @@ private slots:
void RepositionContexts();
+ void GroupNodes();
+
+ void UngroupNodes();
+
};
}
diff --git a/app/widget/nodeview/nodeviewcontext.cpp b/app/widget/nodeview/nodeviewcontext.cpp
new file mode 100644
index 000000000..ad1bdc6ac
--- /dev/null
+++ b/app/widget/nodeview/nodeviewcontext.cpp
@@ -0,0 +1,114 @@
+#include "nodeviewcontext.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "node/block/block.h"
+#include "node/output/track/track.h"
+#include "nodeviewitem.h"
+#include "ui/colorcoding.h"
+
+namespace olive {
+
+#define super QGraphicsRectItem
+
+NodeViewContext::NodeViewContext(QGraphicsItem *item) :
+ super(item)
+{
+ setFlag(ItemIsMovable);
+ setFlag(ItemIsSelectable);
+
+ // Set default label text
+ SetContext(nullptr);
+}
+
+void NodeViewContext::SetContext(Node *node)
+{
+ context_ = node;
+
+ if (context_) {
+ if (Block *block = dynamic_cast(node)) {
+ lbl_ = QCoreApplication::translate("NodeViewContext",
+ "%1 [%2] :: %3 - %4").arg(block->GetLabelAndName(),
+ Track::Reference::TypeToTranslatedString(block->track()->type()),
+ block->in().toString(),
+ block->out().toString());
+ } else {
+ lbl_ = node->GetLabelAndName();
+ }
+
+ Color c = node->color();
+ setPen(QPen(c.toQColor(), 2));
+
+ c.set_alpha(0.5f);
+ setBrush(c.toQColor());
+ } else {
+ lbl_ = QCoreApplication::translate("NodeViewContext", "(None)");
+ }
+}
+
+void NodeViewContext::AddChild(Node *node)
+{
+ NodeViewItem *item = new NodeViewItem(this);
+ item->SetNode(node);
+}
+
+qreal GetTextOffset(const QFontMetricsF &fm)
+{
+ return fm.height()/2;
+}
+
+void NodeViewContext::UpdateRect()
+{
+ QFont f;
+ QFontMetricsF fm(f);
+ qreal lbl_offset = GetTextOffset(fm);
+
+ QRectF rect = childrenBoundingRect();
+ int pad = NodeViewItem::DefaultItemHeight();
+ rect.adjust(-pad, - lbl_offset*2 - fm.height() - pad, pad, pad);
+ setRect(rect);
+}
+
+void NodeViewContext::SetFlowDirection(NodeViewCommon::FlowDirection dir)
+{
+ auto children = childItems();
+ foreach (auto child, children) {
+ if (NodeViewItem *item = dynamic_cast(child)) {
+ item->SetFlowDirection(dir);
+ }
+ }
+}
+
+void NodeViewContext::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ QPen p = pen();
+
+ if (option->state & QStyle::State_Selected) {
+ p.setStyle(Qt::DotLine);
+ }
+
+ painter->setPen(p);
+ painter->setBrush(brush());
+
+ int rounded = painter->fontMetrics().height();
+ painter->drawRoundedRect(rect(), rounded, rounded);
+
+ painter->setPen(context_ ? ColorCoding::GetUISelectorColor(context_->color()) : Qt::white);
+
+ int offset = GetTextOffset(painter->fontMetrics());
+
+ QRectF text_rect = rect();
+ text_rect.adjust(offset, offset, -offset, -offset);
+ painter->drawText(text_rect, lbl_);
+}
+
+QVariant NodeViewContext::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ return super::itemChange(change, value);
+}
+
+}
diff --git a/app/widget/nodeview/nodeviewcontext.h b/app/widget/nodeview/nodeviewcontext.h
new file mode 100644
index 000000000..4e1a8a866
--- /dev/null
+++ b/app/widget/nodeview/nodeviewcontext.h
@@ -0,0 +1,39 @@
+#ifndef NODEVIEWCONTEXT_H
+#define NODEVIEWCONTEXT_H
+
+#include
+#include
+
+#include "node/node.h"
+#include "nodeviewcommon.h"
+
+namespace olive {
+
+class NodeViewContext : public QGraphicsRectItem
+{
+public:
+ NodeViewContext(QGraphicsItem *item = nullptr);
+
+ void SetContext(Node *node);
+
+ void AddChild(Node *node);
+
+ void UpdateRect();
+
+ void SetFlowDirection(NodeViewCommon::FlowDirection dir);
+
+ virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
+
+protected:
+ virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
+
+private:
+ Node *context_;
+
+ QString lbl_;
+
+};
+
+}
+
+#endif // NODEVIEWCONTEXT_H
diff --git a/app/widget/nodeview/nodeviewitem.cpp b/app/widget/nodeview/nodeviewitem.cpp
index 73e2d8019..fa362af72 100644
--- a/app/widget/nodeview/nodeviewitem.cpp
+++ b/app/widget/nodeview/nodeviewitem.cpp
@@ -406,6 +406,10 @@ QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
{
if (change == ItemPositionHasChanged && node_) {
ReadjustAllEdges();
+
+ if (NodeViewContext *ctx = dynamic_cast(parentItem())) {
+ ctx->UpdateRect();
+ }
}
return QGraphicsItem::itemChange(change, value);
diff --git a/app/widget/nodeview/nodeviewscene.cpp b/app/widget/nodeview/nodeviewscene.cpp
index 878fd52a4..56de3d250 100644
--- a/app/widget/nodeview/nodeviewscene.cpp
+++ b/app/widget/nodeview/nodeviewscene.cpp
@@ -194,6 +194,31 @@ void NodeViewScene::RemoveEdge(Node *output, const NodeInput &input)
}
}
+NodeViewContext *NodeViewScene::AddContext(Node *node)
+{
+ NodeViewContext *context_item = context_map_.value(node);
+
+ if (!context_item) {
+ context_item = new NodeViewContext();
+ context_item->SetContext(node);
+ context_item->setPos(0, 0);
+ addItem(context_item);
+
+ const NodeGraph::PositionMap &map = node->parent()->GetNodesForContext(node);
+ for (auto it=map.cbegin(); it!=map.cend(); it++) {
+ context_item->AddChild(it.key());
+ }
+ context_item->UpdateRect();
+ }
+
+ return context_item;
+}
+
+void NodeViewScene::RemoveContext(Node *node)
+{
+ delete context_map_.take(node);
+}
+
int NodeViewScene::DetermineWeight(Node *n)
{
QVector inputs = n->GetImmediateDependencies();
diff --git a/app/widget/nodeview/nodeviewscene.h b/app/widget/nodeview/nodeviewscene.h
index 29cf5bbb7..4f6357dee 100644
--- a/app/widget/nodeview/nodeviewscene.h
+++ b/app/widget/nodeview/nodeviewscene.h
@@ -25,6 +25,7 @@
#include
#include "node/graph.h"
+#include "nodeviewcontext.h"
#include "nodeviewedge.h"
#include "nodeviewitem.h"
@@ -99,6 +100,9 @@ public slots:
NodeViewEdge *AddEdge(Node *output, const NodeInput& input);
void RemoveEdge(Node *output, const NodeInput& input);
+ NodeViewContext *AddContext(Node *node);
+ void RemoveContext(Node *node);
+
/**
* @brief Set whether edges in this scene should be curved or not
*/
@@ -113,6 +117,8 @@ private:
void DisconnectNode(Node *n);
+ QHash context_map_;
+
QHash item_map_;
QVector edges_;