implemented saving/loading for groups

This commit is contained in:
itsmattkc
2021-12-28 21:52:28 -08:00
parent 74d9c43ff5
commit a4934f1d47
14 changed files with 170 additions and 94 deletions
+44 -21
View File
@@ -24,15 +24,30 @@
#include "node/factory.h"
#include "widget/nodeparamview/nodeparamviewundo.h"
#include "widget/nodeview/nodeviewundo.h"
//#include "widget/timelinewidget/undo/timelineundogeneral.h"
namespace olive {
void XMLConnectNodes(const XMLNodeData &xml_node_data, uint version, MultiUndoCommand *command)
bool XMLReadNextStartElement(QXmlStreamReader *reader)
{
foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) {
Node *out = xml_node_data.node_ptrs.value(con.output_node);
QXmlStreamReader::TokenType token;
if (out) {
while ((token = reader->readNext()) != QXmlStreamReader::Invalid
&& token != QXmlStreamReader::EndDocument) {
if (reader->isEndElement()) {
return false;
} else if (reader->isStartElement()) {
return true;
}
}
return false;
}
void XMLNodeData::PostConnect(uint version, MultiUndoCommand *command) const
{
foreach (const XMLNodeData::SerializedConnection& con, desired_connections) {
if (Node *out = node_ptrs.value(con.output_node)) {
// Use output param as hint tag since we grandfathered those in
Node::ValueHint hint(con.output_param);
@@ -53,28 +68,36 @@ void XMLConnectNodes(const XMLNodeData &xml_node_data, uint version, MultiUndoCo
}
}
}
}
bool XMLReadNextStartElement(QXmlStreamReader *reader)
{
QXmlStreamReader::TokenType token;
while ((token = reader->readNext()) != QXmlStreamReader::Invalid
&& token != QXmlStreamReader::EndDocument) {
if (reader->isEndElement()) {
return false;
} else if (reader->isStartElement()) {
return true;
foreach (const XMLNodeData::BlockLink& l, block_links) {
Node *a = l.block;
Node *b = node_ptrs.value(l.link);
if (command) {
command->add_child(new NodeLinkCommand(a, b, true));
} else {
Node::Link(a, b);
}
}
return false;
}
foreach (const XMLNodeData::GroupLink &l, group_input_links) {
if (Node *input_node = node_ptrs.value(l.input_node)) {
NodeInput resolved(input_node, l.input_id, l.input_element);
if (command) {
command->add_child(new NodeGroupAddInputPassthrough(l.group, resolved));
} else {
l.group->AddInputPassthrough(resolved);
}
}
}
void XMLLinkBlocks(const XMLNodeData &xml_node_data)
{
foreach (const XMLNodeData::BlockLink& l, xml_node_data.block_links) {
Block::Link(l.block, static_cast<Block*>(xml_node_data.node_ptrs.value(l.link)));
for (auto it=group_output_links.cbegin(); it!=group_output_links.cend(); it++) {
if (Node *output_node = node_ptrs.value(it.value())) {
if (command) {
command->add_child(new NodeGroupSetOutputPassthrough(it.key(), output_node));
} else {
it.key()->SetOutputPassthrough(output_node);
}
}
}
}
+12 -4
View File
@@ -31,6 +31,7 @@ namespace olive {
class Block;
class Node;
class NodeInput;
class NodeGroup;
#define XMLAttributeLoop(reader, item) \
foreach (const QXmlStreamAttribute& item, reader->attributes())
@@ -49,14 +50,23 @@ struct XMLNodeData {
quintptr link;
};
struct GroupLink {
NodeGroup *group;
quintptr input_node;
QString input_id;
int input_element;
};
QHash<quintptr, Node*> node_ptrs;
QList<SerializedConnection> desired_connections;
QList<BlockLink> block_links;
QVector<GroupLink> group_input_links;
QHash<NodeGroup*, quintptr> group_output_links;
void PostConnect(uint version, MultiUndoCommand *command = nullptr) const;
};
void XMLConnectNodes(const XMLNodeData& xml_node_data, uint version, MultiUndoCommand *command = nullptr);
/**
* @brief Workaround for QXmlStreamReader::readNextStartElement not detecting the end of a document
*
@@ -68,8 +78,6 @@ void XMLConnectNodes(const XMLNodeData& xml_node_data, uint version, MultiUndoCo
*/
bool XMLReadNextStartElement(QXmlStreamReader* reader);
void XMLLinkBlocks(const XMLNodeData& xml_node_data);
}
#endif // XMLREADLOOP_H
+1 -1
View File
@@ -75,7 +75,7 @@
namespace olive {
Core* Core::instance_ = nullptr;
const uint Core::kProjectVersion = 210907;
const uint Core::kProjectVersion = 211228;
Core::Core(const CoreParams& params) :
main_window_(nullptr),
+11
View File
@@ -54,6 +54,7 @@
namespace olive {
QList<Node*> NodeFactory::library_;
QVector<int> NodeFactory::hidden_;
void NodeFactory::Initialize()
{
@@ -65,6 +66,9 @@ void NodeFactory::Initialize()
library_.append(created_node);
}
hidden_.append(kTextGeneratorLegacy);
hidden_.append(kGroupNode);
}
void NodeFactory::Destroy()
@@ -86,6 +90,11 @@ Menu *NodeFactory::CreateMenu(QWidget* parent, bool create_none_item, Node::Cate
continue;
}
if (hidden_.contains(i)) {
// Skip this node
continue;
}
// Make sure nodes are up-to-date with the current translation
n->Retranslate();
@@ -241,6 +250,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new SubtitleBlock();
case kShapeGenerator:
return new ShapeNode();
case kGroupNode:
return new NodeGroup();
case kInternalNodeCount:
break;
+3
View File
@@ -61,6 +61,7 @@ public:
kTimeRemapNode,
kSubtitleBlock,
kShapeGenerator,
kGroupNode,
// Count value
kInternalNodeCount
@@ -87,6 +88,8 @@ public:
private:
static QList<Node*> library_;
static QVector<int> hidden_;
};
}
+52 -10
View File
@@ -24,6 +24,8 @@
namespace olive {
#define super Node
NodeGroup::NodeGroup() :
output_passthrough_(nullptr)
{
@@ -31,11 +33,7 @@ NodeGroup::NodeGroup() :
QString NodeGroup::Name() const
{
if (custom_name_.isEmpty()) {
return tr("Group");
} else {
return custom_name_;
}
return tr("Group");
}
QString NodeGroup::id() const
@@ -131,15 +129,59 @@ QString NodeGroup::GetInputName(const QString &id) const
return input_passthroughs_.value(id).name();
}
void NodeGroupSetCustomNameCommand::redo()
bool NodeGroup::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled)
{
old_name_ = group_->GetCustomName();
group_->SetCustomName(new_name_);
if (reader->name() == QStringLiteral("inputpassthroughs")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("inputpassthrough")) {
XMLNodeData::GroupLink link;
link.group = this;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
link.input_node = reader->readElementText().toULongLong();
} else if (reader->name() == QStringLiteral("input")) {
link.input_id = reader->readElementText();
} else if (reader->name() == QStringLiteral("element")) {
link.input_element = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
}
xml_node_data.group_input_links.append(link);
} else {
reader->skipCurrentElement();
}
}
return true;
} else if (reader->name() == QStringLiteral("outputpassthrough")) {
xml_node_data.group_output_links.insert(this, reader->readElementText().toULongLong());
return true;
} else {
return super::LoadCustom(reader, xml_node_data, version, cancelled);
}
}
void NodeGroupSetCustomNameCommand::undo()
void NodeGroup::SaveCustom(QXmlStreamWriter *writer) const
{
group_->SetCustomName(old_name_);
super::SaveCustom(writer);
writer->writeStartElement(QStringLiteral("inputpassthroughs"));
foreach (const NodeInput &ip, input_passthroughs_) {
writer->writeStartElement(QStringLiteral("inputpassthrough"));
writer->writeTextElement(QStringLiteral("node"), QString::number(reinterpret_cast<quintptr>(ip.node())));
writer->writeTextElement(QStringLiteral("input"), ip.input());
writer->writeTextElement(QStringLiteral("element"), QString::number(ip.element()));
writer->writeEndElement(); // input
}
writer->writeEndElement(); // inputpassthroughs
writer->writeTextElement(QStringLiteral("outputpassthrough"), QString::number(reinterpret_cast<quintptr>(output_passthrough_)));
}
void NodeGroupAddInputPassthrough::redo()
+5 -47
View File
@@ -52,24 +52,6 @@ public:
void SetOutputPassthrough(Node *node);
const QString &GetCustomName() const
{
return custom_name_;
}
void SetCustomName(const QString &name)
{
custom_name_ = name;
// NOTE: Not technically the right signal, but should achieve the right goal
emit LabelChanged(custom_name_);
}
void ClearCustomName()
{
custom_name_.clear();
}
static QString GetGroupInputIDFromInput(const NodeInput &input);
const QHash<QString, NodeInput> &GetInputPassthroughs() const
@@ -88,40 +70,16 @@ signals:
void OutputPassthroughChanged(NodeGroup *group, Node *output);
protected:
virtual bool LoadCustom(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled) override;
virtual void SaveCustom(QXmlStreamWriter* writer) const override;
private:
QHash<QString, NodeInput> input_passthroughs_;
Node *output_passthrough_;
QString custom_name_;
};
class NodeGroupSetCustomNameCommand : public UndoCommand
{
public:
NodeGroupSetCustomNameCommand(NodeGroup *group, const QString &name) :
group_(group),
new_name_(name)
{}
virtual Project * GetRelevantProject() const override
{
return group_->project();
}
protected:
virtual void redo() override;
virtual void undo() override;
private:
NodeGroup *group_;
QString old_name_;
QString new_name_;
};
class NodeGroupAddInputPassthrough : public UndoCommand
+1 -6
View File
@@ -205,12 +205,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
}
// Make connections
if (!xml_node_data.desired_connections.isEmpty()) {
XMLConnectNodes(xml_node_data, data_version, command);
}
// Link blocks
XMLLinkBlocks(xml_node_data);
xml_node_data.PostConnect(data_version, command);
// Process contexts
for (auto it=pasted_contexts.cbegin(); it!=pasted_contexts.cend(); it++) {
+1 -4
View File
@@ -193,10 +193,7 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
}
// Make connections
XMLConnectNodes(xml_node_data, version);
// Link blocks
XMLLinkBlocks(xml_node_data);
xml_node_data.PostConnect(version);
}
void Project::Save(QXmlStreamWriter *writer) const
+1
View File
@@ -31,6 +31,7 @@ NodePanel::NodePanel(QWidget *parent) :
// Connect node view signals to this panel - MAY REMOVE
connect(node_widget_->view(), &NodeView::NodesSelected, this, &NodePanel::NodesSelected);
connect(node_widget_->view(), &NodeView::NodesDeselected, this, &NodePanel::NodesDeselected);
connect(node_widget_->view(), &NodeView::NodeGroupOpenRequested, this, &NodePanel::NodeGroupOpenRequested);
// Set it as the main widget of this panel
SetWidgetWithPadding(node_widget_);
+8
View File
@@ -40,6 +40,11 @@ public:
return node_widget_;
}
const QVector<Node*> &GetContexts() const
{
return node_widget_->view()->GetContexts();
}
void SetContexts(const QVector<Node*> &nodes)
{
node_widget_->SetContexts(nodes);
@@ -109,6 +114,7 @@ public slots:
void Select(const QVector<Node*>& nodes, bool center_view_on_item)
{
node_widget_->view()->Select(nodes, center_view_on_item);
this->raise();
}
signals:
@@ -116,6 +122,8 @@ signals:
void NodesDeselected(const QVector<Node*>& nodes);
void NodeGroupOpenRequested(NodeGroup *group);
private:
virtual void Retranslate() override
{
+10
View File
@@ -38,6 +38,16 @@ public:
return static_cast<NodeParamView *>(GetTimeBasedWidget());
}
void SetCreateCheckBoxes(NodeParamViewCheckBoxBehavior e)
{
GetParamView()->SetCreateCheckBoxes(e);
}
void SetIgnoreNodeFlags(bool e)
{
GetParamView()->SetIgnoreNodeFlags(e);
}
public slots:
void SelectNodes(const QVector<Node*>& nodes);
void DeselectNodes(const QVector<Node*>& nodes);
+19 -1
View File
@@ -93,6 +93,7 @@ MainWindow::MainWindow(QWidget *parent) :
// Make node-related connections
connect(node_panel_, &NodePanel::NodesSelected, param_panel_, &ParamPanel::SelectNodes);
connect(node_panel_, &NodePanel::NodesDeselected, param_panel_, &ParamPanel::DeselectNodes);
connect(node_panel_, &NodePanel::NodeGroupOpenRequested, this, &MainWindow::NodeGroupRequested);
connect(param_panel_, &ParamPanel::RequestSelectNode, this, [this](const QVector<Node*>& target){
node_panel_->Select(target, true);
});
@@ -451,6 +452,17 @@ void MainWindow::StatusBarDoubleClicked()
task_man_panel_->raise();
}
void MainWindow::NodeGroupRequested(NodeGroup *group)
{
NodePanel *panel = new NodePanel(this);
panel->setFloating(true);
panel->setVisible(true);
panel->SetContexts({group});
panel->SetSignalInsteadOfClose(true);
addDockWidget(Qt::LeftDockWidgetArea, panel);
connect(panel, &NodePanel::CloseRequested, panel, &NodePanel::deleteLater);
}
void MainWindow::TimelinePanelSelectionChanged(const QVector<Block *> &blocks)
{
TimelinePanel *panel = static_cast<TimelinePanel *>(sender());
@@ -723,7 +735,13 @@ void MainWindow::FocusedPanelChanged(PanelWidget *panel)
UpdateAudioMonitorParams(tbp->GetConnectedViewer());
}
if (TimelinePanel* timeline = dynamic_cast<TimelinePanel*>(panel)) {
if (NodePanel *node_panel = dynamic_cast<NodePanel*>(panel)) {
// Set param view contexts to these
bool is_default_node_panel = node_panel == node_panel_;
param_panel_->SetIgnoreNodeFlags(!is_default_node_panel);
param_panel_->SetCreateCheckBoxes(is_default_node_panel ? kNoCheckBoxes : kCheckBoxesOnNonConnected);
param_panel_->SetContexts(node_panel->GetContexts());
} else if (TimelinePanel* timeline = dynamic_cast<TimelinePanel*>(panel)) {
// Signal timeline focus
TimelineFocused(timeline->GetConnectedViewer());
+2
View File
@@ -187,6 +187,8 @@ private slots:
void StatusBarDoubleClicked();
void NodeGroupRequested(NodeGroup *group);
#ifdef Q_OS_LINUX
void ShowNouveauWarning();
#endif