From 893adc3d7241236fc4b7aadc130e4548c6b16d69 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Mon, 12 Apr 2021 16:35:10 +1000 Subject: [PATCH] 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 --- app/node/factory.cpp | 3 + app/node/factory.h | 1 + app/node/input/CMakeLists.txt | 1 + app/node/input/multicam/multicamnode.cpp | 6 ++ app/node/input/multicam/multicamnode.h | 11 +++ app/node/input/value/CMakeLists.txt | 22 +++++ app/node/input/value/valuenode.cpp | 81 +++++++++++++++++++ app/node/input/value/valuenode.h | 78 ++++++++++++++++++ app/node/inputimmediate.cpp | 18 +++-- app/node/inputimmediate.h | 7 ++ app/node/node.cpp | 18 ++++- .../nodeparamview/nodeparamviewitem.cpp | 32 +++++++- app/widget/nodeparamview/nodeparamviewitem.h | 8 ++ .../nodeparamviewwidgetbridge.cpp | 17 ++++ .../nodeparamview/nodeparamviewwidgetbridge.h | 4 + 15 files changed, 293 insertions(+), 14 deletions(-) create mode 100644 app/node/input/multicam/multicamnode.cpp create mode 100644 app/node/input/multicam/multicamnode.h create mode 100644 app/node/input/value/CMakeLists.txt create mode 100644 app/node/input/value/valuenode.cpp create mode 100644 app/node/input/value/valuenode.h diff --git a/app/node/factory.cpp b/app/node/factory.cpp index d0c468893..33c5a8996 100644 --- a/app/node/factory.cpp +++ b/app/node/factory.cpp @@ -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 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; diff --git a/app/node/factory.h b/app/node/factory.h index 78ef3bfbe..7d19977ce 100644 --- a/app/node/factory.h +++ b/app/node/factory.h @@ -56,6 +56,7 @@ public: kProjectFootage, kProjectFolder, kProjectSequence, + kValueNode, // Count value kInternalNodeCount diff --git a/app/node/input/CMakeLists.txt b/app/node/input/CMakeLists.txt index 5c95bb00d..c07a2bed6 100644 --- a/app/node/input/CMakeLists.txt +++ b/app/node/input/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see . add_subdirectory(time) +add_subdirectory(value) set(OLIVE_SOURCES ${OLIVE_SOURCES} diff --git a/app/node/input/multicam/multicamnode.cpp b/app/node/input/multicam/multicamnode.cpp new file mode 100644 index 000000000..0c1273cb5 --- /dev/null +++ b/app/node/input/multicam/multicamnode.cpp @@ -0,0 +1,6 @@ +#include "multicamnode.h" + +MultiCamNode::MultiCamNode() +{ + +} diff --git a/app/node/input/multicam/multicamnode.h b/app/node/input/multicam/multicamnode.h new file mode 100644 index 000000000..c21e7f6ca --- /dev/null +++ b/app/node/input/multicam/multicamnode.h @@ -0,0 +1,11 @@ +#ifndef MULTICAMNODE_H +#define MULTICAMNODE_H + + +class MultiCamNode +{ +public: + MultiCamNode(); +}; + +#endif // MULTICAMNODE_H diff --git a/app/node/input/value/CMakeLists.txt b/app/node/input/value/CMakeLists.txt new file mode 100644 index 000000000..52ea76185 --- /dev/null +++ b/app/node/input/value/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + node/input/value/valuenode.h + node/input/value/valuenode.cpp + PARENT_SCOPE +) diff --git a/app/node/input/value/valuenode.cpp b/app/node/input/value/valuenode.cpp new file mode 100644 index 000000000..5143c6ac8 --- /dev/null +++ b/app/node/input/value/valuenode.cpp @@ -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 . + +***/ + +#include "valuenode.h" + +namespace olive { + +const QString ValueNode::kTypeInput = QStringLiteral("type_in"); +const QString ValueNode::kValueInput = QStringLiteral("value_in"); +const QVector 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); +} + +} diff --git a/app/node/input/value/valuenode.h b/app/node/input/value/valuenode.h new file mode 100644 index 000000000..6d2a6b78e --- /dev/null +++ b/app/node/input/value/valuenode.h @@ -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 . + +***/ + +#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 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 kSupportedTypes; + +}; + +} + +#endif // VALUENODE_H diff --git a/app/node/inputimmediate.cpp b/app/node/inputimmediate.cpp index d3fae09ae..e0d2a0187 100644 --- a/app/node/inputimmediate.cpp +++ b/app/node/inputimmediate.cpp @@ -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; diff --git a/app/node/inputimmediate.h b/app/node/inputimmediate.h index ff6c2794c..e69467f31 100644 --- a/app/node/inputimmediate.h +++ b/app/node/inputimmediate.h @@ -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 * diff --git a/app/node/node.cpp b/app/node/node.cpp index 1f4f4326b..c0678db5a 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -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; iset_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 &Node::GetKeyframeTracks(const QString &input, int element) const diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index a9463b6e8..fe92606f7 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -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; iwidgets().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; iwidgets().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(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), diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index b8c9b0a19..3351682dd 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -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 diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 17411ace1..9c0254344 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -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) diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 7b191f143..ee288f35f 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -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); + }; }