implemented value node

Also implements changes necessary to support an input data type
that changes. Much of that foundation was built in
`nodearchchanges`, but hadn't been finalized. This commit
finalizes and provides a reference implementation/test with the
"Value" node.

Fixes #1443
This commit is contained in:
itsmattkc
2021-04-12 16:37:26 +10:00
parent 6d0e188393
commit 893adc3d72
15 changed files with 293 additions and 14 deletions
+3
View File
@@ -46,6 +46,7 @@
#include "project/folder/folder.h"
#include "project/footage/footage.h"
#include "project/sequence/sequence.h"
#include "node/input/value/valuenode.h"
namespace olive {
QList<Node*> NodeFactory::library_;
@@ -234,6 +235,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new Folder();
case kProjectSequence:
return new Sequence();
case kValueNode:
return new ValueNode();
case kInternalNodeCount:
break;
+1
View File
@@ -56,6 +56,7 @@ public:
kProjectFootage,
kProjectFolder,
kProjectSequence,
kValueNode,
// Count value
kInternalNodeCount
+1
View File
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(time)
add_subdirectory(value)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
+6
View File
@@ -0,0 +1,6 @@
#include "multicamnode.h"
MultiCamNode::MultiCamNode()
{
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef MULTICAMNODE_H
#define MULTICAMNODE_H
class MultiCamNode
{
public:
MultiCamNode();
};
#endif // MULTICAMNODE_H
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/input/value/valuenode.h
node/input/value/valuenode.cpp
PARENT_SCOPE
)
+81
View File
@@ -0,0 +1,81 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
***/
#include "valuenode.h"
namespace olive {
const QString ValueNode::kTypeInput = QStringLiteral("type_in");
const QString ValueNode::kValueInput = QStringLiteral("value_in");
const QVector<NodeValue::Type> ValueNode::kSupportedTypes = {
NodeValue::kFloat,
NodeValue::kInt,
NodeValue::kRational,
NodeValue::kVec2,
NodeValue::kVec3,
NodeValue::kVec4,
NodeValue::kColor,
NodeValue::kText,
NodeValue::kMatrix,
NodeValue::kFont,
};
#define super Node
ValueNode::ValueNode()
{
AddInput(kTypeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kValueInput, kSupportedTypes.first(), QVariant(), InputFlags(kInputFlagNotConnectable));
}
void ValueNode::Retranslate()
{
SetInputName(kTypeInput, QStringLiteral("Type"));
SetInputName(kValueInput, QStringLiteral("Value"));
QStringList type_names;
type_names.reserve(kSupportedTypes.size());
foreach (NodeValue::Type type, kSupportedTypes) {
type_names.append(NodeValue::GetPrettyDataTypeName(type));
}
SetComboBoxStrings(kTypeInput, type_names);
}
NodeValueTable ValueNode::Value(const QString &output, NodeValueDatabase &value) const
{
Q_UNUSED(output)
// Pop combobox value off table because no other node will need it
value[kTypeInput].Take(NodeValue::kCombo);
return value.Merge();
}
void ValueNode::InputValueChangedEvent(const QString &input, int element)
{
if (input == kTypeInput) {
SetInputDataType(kValueInput, kSupportedTypes.at(GetStandardValue(kTypeInput).toInt()));
}
super::InputValueChangedEvent(input, element);
}
}
+78
View File
@@ -0,0 +1,78 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
***/
#ifndef VALUENODE_H
#define VALUENODE_H
#include "node/node.h"
namespace olive {
class ValueNode : public Node
{
Q_OBJECT
public:
ValueNode();
NODE_DEFAULT_DESTRUCTOR(ValueNode)
virtual Node* copy() const override
{
return new ValueNode();
}
virtual QString Name() const override
{
return tr("Value");
}
virtual QString id() const override
{
return QStringLiteral("org.olivevideoeditor.Olive.value");
}
virtual QVector<CategoryID> Category() const override
{
return {kCategoryInput};
}
virtual QString Description() const override
{
return tr("Create a single value that can be connected to various other inputs.");
}
static const QString kTypeInput;
static const QString kValueInput;
virtual void Retranslate() override;
virtual NodeValueTable Value(const QString &output, NodeValueDatabase &value) const override;
protected:
virtual void InputValueChangedEvent(const QString &input, int element) override;
private:
static const QVector<NodeValue::Type> kSupportedTypes;
};
}
#endif // VALUENODE_H
+12 -6
View File
@@ -27,14 +27,10 @@
namespace olive {
NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const SplitValue &default_val) :
default_value_(default_val),
keyframing_(false)
{
int track_size = NodeValue::get_number_of_keyframe_tracks(type);
keyframe_tracks_.resize(track_size);
standard_value_.resize(track_size);
set_split_standard_value(default_val);
set_data_type(type);
}
void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int track)
@@ -179,6 +175,16 @@ bool NodeInputImmediate::has_keyframe_at_time(const rational &time) const
return false;
}
void NodeInputImmediate::set_data_type(NodeValue::Type type)
{
int track_size = NodeValue::get_number_of_keyframe_tracks(type);
keyframe_tracks_.resize(track_size);
standard_value_.resize(track_size);
set_split_standard_value(default_value_);
}
NodeKeyframe *NodeInputImmediate::get_earliest_keyframe() const
{
NodeKeyframe* earliest = nullptr;
+7
View File
@@ -151,12 +151,19 @@ public:
return (!is_keyframing() || keyframe_tracks_.at(track).isEmpty());
}
void set_data_type(NodeValue::Type type);
private:
/**
* @brief Non-keyframed value
*/
SplitValue standard_value_;
/**
* @brief Default value
*/
SplitValue default_value_;
/**
* @brief Internal keyframe array
*
+14 -4
View File
@@ -505,10 +505,15 @@ NodeValue::Type Node::GetInputDataType(const QString &id) const
void Node::SetInputDataType(const QString &id, const NodeValue::Type &type)
{
Input* i = GetInternalInputData(id);
Input* input_meta = GetInternalInputData(id);
if (i) {
i->type = type;
if (input_meta) {
input_meta->type = type;
int array_sz = InputArraySize(id);
for (int i=-1; i<array_sz; i++) {
GetImmediate(id, i)->set_data_type(type);
}
emit InputDataTypeChanged(id, type);
} else {
@@ -693,7 +698,12 @@ SplitValue Node::GetSplitDefaultValue(const QString &input) const
QVariant Node::GetSplitDefaultValueOnTrack(const QString &input, int track) const
{
return GetSplitDefaultValue(input).at(track);
SplitValue val = GetSplitDefaultValue(input);
if (track < val.size()) {
return val.at(track);
} else {
return QVariant();
}
}
const QVector<NodeKeyframeTrack> &Node::GetKeyframeTracks(const QString &input, int element) const
+28 -4
View File
@@ -36,6 +36,9 @@ const int NodeParamViewItemBody::kKeyControlColumn = 10;
const int NodeParamViewItemBody::kArrayInsertColumn = kKeyControlColumn-1;
const int NodeParamViewItemBody::kArrayRemoveColumn = kArrayInsertColumn-1;
// 0 is for the array collapse button, 1 is for the main label, widgets start at 2
const int NodeParamViewItemBody::kWidgetStartColumn = 2;
#define super QDockWidget
NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) :
@@ -261,6 +264,10 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, Node *node, const
InputUI ui_objects;
// Store layout and row
ui_objects.layout = layout;
ui_objects.row = row;
// Add descriptor label
ui_objects.main_label = new QLabel();
@@ -303,23 +310,24 @@ void NodeParamViewItemBody::CreateWidgets(QGridLayout* layout, Node *node, const
// Create a widget/input bridge for this input
ui_objects.widget_bridge = new NodeParamViewWidgetBridge(NodeInput(node, input, element), this);
connect(ui_objects.widget_bridge, &NodeParamViewWidgetBridge::WidgetsRecreated, this, &NodeParamViewItemBody::ReplaceWidgets);
connect(ui_objects.widget_bridge, &NodeParamViewWidgetBridge::ArrayWidgetDoubleClicked, this, &NodeParamViewItemBody::ToggleArrayExpanded);
// 0 is for the array collapse button, 1 is for the main label, widgets start at 2
const int widget_start = 2;
// Place widgets into layout
PlaceWidgetsFromBridge(layout, ui_objects.widget_bridge, row);
// Add widgets for this parameter to the layout
for (int i=0; i<ui_objects.widget_bridge->widgets().size(); i++) {
QWidget* w = ui_objects.widget_bridge->widgets().at(i);
layout->addWidget(w, row, i+widget_start);
layout->addWidget(w, row, i+kWidgetStartColumn);
}
if (node->IsInputConnectable(input)) {
// Create clickable label used when an input is connected
ui_objects.connected_label = new NodeParamViewConnectedLabel(input_ref);
connect(ui_objects.connected_label, &NodeParamViewConnectedLabel::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode);
layout->addWidget(ui_objects.connected_label, row, widget_start);
layout->addWidget(ui_objects.connected_label, row, kWidgetStartColumn);
}
// Add keyframe control to this layout if parameter is keyframable
@@ -418,6 +426,16 @@ void NodeParamViewItemBody::UpdateUIForEdgeConnection(const NodeInput& input)
}
}
void NodeParamViewItemBody::PlaceWidgetsFromBridge(QGridLayout* layout, NodeParamViewWidgetBridge *bridge, int row)
{
// Add widgets for this parameter to the layout
for (int i=0; i<bridge->widgets().size(); i++) {
QWidget* w = bridge->widgets().at(i);
layout->addWidget(w, row, i+kWidgetStartColumn);
}
}
void NodeParamViewItemBody::ArrayCollapseBtnPressed(bool checked)
{
const NodeInputPair& input = array_collapse_buttons_.key(static_cast<CollapseButton*>(sender()));
@@ -514,6 +532,12 @@ void NodeParamViewItemBody::ToggleArrayExpanded()
}
}
void NodeParamViewItemBody::ReplaceWidgets(const NodeInput &input)
{
InputUI ui = input_ui_map_.value(input);
PlaceWidgetsFromBridge(ui.layout, ui.widget_bridge, ui.row);
}
NodeParamViewItemBody::InputUI::InputUI() :
main_label(nullptr),
widget_bridge(nullptr),
@@ -97,6 +97,8 @@ private:
void UpdateUIForEdgeConnection(const NodeInput &input);
void PlaceWidgetsFromBridge(QGridLayout *layout, NodeParamViewWidgetBridge* bridge, int row);
struct InputUI {
InputUI();
@@ -104,6 +106,8 @@ private:
NodeParamViewWidgetBridge* widget_bridge;
NodeParamViewConnectedLabel* connected_label;
NodeParamViewKeyframeControl* key_control;
QGridLayout* layout;
int row;
NodeParamViewArrayButton* array_insert_btn;
NodeParamViewArrayButton* array_remove_btn;
@@ -132,6 +136,8 @@ private:
static const int kArrayInsertColumn;
static const int kArrayRemoveColumn;
static const int kWidgetStartColumn;
private slots:
void EdgeChanged(const NodeOutput &output, const NodeInput &input);
@@ -147,6 +153,8 @@ private slots:
void ToggleArrayExpanded();
void ReplaceWidgets(const NodeInput& input);
};
class NodeParamViewItem : public QDockWidget
@@ -49,6 +49,7 @@ NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(const NodeInput &input, QOb
connect(input_.node(), &Node::ValueChanged, this, &NodeParamViewWidgetBridge::InputValueChanged);
connect(input_.node(), &Node::InputPropertyChanged, this, &NodeParamViewWidgetBridge::PropertyChanged);
connect(input_.node(), &Node::InputDataTypeChanged, this, &NodeParamViewWidgetBridge::InputDataTypeChanged);
}
void NodeParamViewWidgetBridge::SetTime(const rational &time)
@@ -756,6 +757,22 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString& input, const QStr
}
}
void NodeParamViewWidgetBridge::InputDataTypeChanged(const QString &input, NodeValue::Type type)
{
Q_UNUSED(type)
if (input == this->input_.input()) {
// Delete all widgets
qDeleteAll(widgets_);
widgets_.clear();
// Create new widgets
CreateWidgets();
// Signal that widgets are new
emit WidgetsRecreated(input_);
}
}
bool NodeParamViewScrollBlocker::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched)
@@ -52,6 +52,8 @@ public:
signals:
void ArrayWidgetDoubleClicked();
void WidgetsRecreated(const NodeInput& input);
private:
void CreateWidgets();
@@ -85,6 +87,8 @@ private slots:
void PropertyChanged(const QString &input, const QString& key, const QVariant& value);
void InputDataTypeChanged(const QString& input, NodeValue::Type type);
};
}