implemented saving/loading for groups
This commit is contained in:
+44
-21
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -187,6 +187,8 @@ private slots:
|
||||
|
||||
void StatusBarDoubleClicked();
|
||||
|
||||
void NodeGroupRequested(NodeGroup *group);
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
void ShowNouveauWarning();
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user