ui for output rows on nodes

This commit is contained in:
itsmattkc
2019-04-12 01:44:49 +10:00
parent a0f3ab2a6b
commit 83620173af
7 changed files with 271 additions and 205 deletions
+4
View File
@@ -128,6 +128,10 @@ Effect::Effect(Clip* c, const EffectMeta *em) :
expanded_(true),
texture_ctx(nullptr)
{
if (em != nullptr) {
// set up UI from effect metadata
name = em->name;
}
}
Effect::~Effect() {
+24 -5
View File
@@ -37,13 +37,18 @@
#include "ui/keyframenavigator.h"
#include "ui/clickablelabel.h"
EffectRow::EffectRow(Effect *parent, const QString &id, const QString &name, bool savable, bool keyframable) :
EffectRow::EffectRow(Effect *parent,
const QString &id,
const QString &name,
bool savable,
bool keyframable) :
QObject(parent),
id_(id),
name_(name),
keyframable_(keyframable),
keyframing_(false),
savable_(savable)
savable_(savable),
output_type_(olive::nodes::kInvalid)
{
Q_ASSERT(parent != nullptr);
@@ -62,7 +67,9 @@ void EffectRow::AddField(EffectField *field)
void EffectRow::AddNodeInput(olive::nodes::DataType type)
{
accepted_datatypes_.append(type);
Q_ASSERT(output_type_ == olive::nodes::kInvalid);
accepted_inputs_.append(type);
}
bool EffectRow::IsKeyframing() {
@@ -107,9 +114,21 @@ void EffectRow::SetEnabled(bool enabled)
}
}
bool EffectRow::CanConnectNodes()
void EffectRow::SetOutputDataType(olive::nodes::DataType type)
{
return !accepted_datatypes_.isEmpty();
Q_ASSERT(accepted_inputs_.isEmpty());
output_type_ = type;
}
bool EffectRow::IsNodeInput()
{
return !accepted_inputs_.isEmpty();
}
bool EffectRow::IsNodeOutput()
{
return output_type_ != olive::nodes::kInvalid;
}
void EffectRow::SetKeyframingEnabled(bool enabled) {
+40 -6
View File
@@ -51,6 +51,7 @@ class ClickableLabel;
class EffectRow : public QObject {
Q_OBJECT
public:
/**
* @brief EffectRow Constructor
*
@@ -82,7 +83,11 @@ public:
* Whether keyframing can be enabled on this row or not. This is true by default. Some values you may want to prevent
* the user from keyframing (e.g. the filename of a VST plugin), which can be done by setting this to false.
*/
EffectRow(Effect* parent, const QString& id, const QString& name, bool savable = true, bool keyframable = true);
EffectRow(Effect* parent,
const QString& id,
const QString& name,
bool savable = true,
bool keyframable = true);
/**
* @brief Retrieve the EffectField at this index. Must be less than FieldCount().
@@ -212,15 +217,33 @@ public:
void SetEnabled(bool enabled);
/**
* @brief Check if nodes can be connected to this input.
* @brief Check if nodes can be connected to this as an input.
*
* Connecting is enabled by adding an accepted node input using AddNodeInput().
*
* @return
*
* TRUE if nodes can be connected.
* TRUE if nodes can be connected as an input.
*/
bool CanConnectNodes();
bool IsNodeInput();
/**
* @brief Check if nodes can be connected to this as an output
*
* Connecting is enabled by setting an output data type in SetOutputDataType().
*
* @return
*
* TRUE if nodes can be connected as an output
*/
bool IsNodeOutput();
/**
* @brief Set output data type
*
* Set the type of data this row outputs to type
*/
void SetOutputDataType(olive::nodes::DataType type);
protected:
/**
@@ -365,9 +388,20 @@ private:
QVector<EffectField*> fields_;
/**
* @brief Internal array of accepted node data types
* @brief Internal array of accepted node data types.
*
* Is mutally-exclusive with accepted_outputs_, i.e. you cannot have values added to this and also a value set in
* accepted_outputs_.
*/
QVector<olive::nodes::DataType> accepted_datatypes_;
QVector<olive::nodes::DataType> accepted_inputs_;
/**
* @brief Internal value for what kind of data this row outputs
*
* Is mutally-exclusive with accepted_inputs_, i.e. you cannot have values added to it and also a value set in
* this.
*/
olive::nodes::DataType output_type_;
};
#endif // EFFECTROW_H
+4
View File
@@ -67,6 +67,10 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em)
opacity->SetMaximum(100);
opacity->SetDefault(100);
// TEMP - Create matrix output
EffectRow* matrix_output = new EffectRow(this, "matrix", "Matrix", false, false);
matrix_output->SetOutputDataType(olive::nodes::kMatrix);
// set up gizmos
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
top_left_gizmo->set_cursor(Qt::SizeFDiagCursor);
+154 -159
View File
@@ -3,189 +3,184 @@
NodeShader::NodeShader(Clip* c, const EffectMeta *em) :
Effect(c, em)
{
if (em != nullptr) {
// set up UI from effect file
name = em->name;
if (em != nullptr && !em->filename.isEmpty() && em->internal == -1) {
QFile effect_file(em->filename);
if (effect_file.open(QFile::ReadOnly)) {
QXmlStreamReader reader(&effect_file);
if (!em->filename.isEmpty() && em->internal == -1) {
QFile effect_file(em->filename);
if (effect_file.open(QFile::ReadOnly)) {
QXmlStreamReader reader(&effect_file);
while (!reader.atEnd()) {
if (reader.name() == "field" && reader.isStartElement()) {
int type = olive::nodes::kInvalid;
QString id;
QString name;
while (!reader.atEnd()) {
if (reader.name() == "field" && reader.isStartElement()) {
int type = olive::nodes::kInvalid;
QString id;
QString name;
// get field type
const QXmlStreamAttributes& attributes = reader.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "type") {
QString comp = attr.value().toString().toUpper();
type = olive::nodes::StringToDataType(comp);
} else if (attr.name() == "id") {
id = attr.value().toString();
} else if (attr.name() == "name") {
name = attr.value().toString();
}
// get field type
const QXmlStreamAttributes& attributes = reader.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "type") {
QString comp = attr.value().toString().toUpper();
type = olive::nodes::StringToDataType(comp);
} else if (attr.name() == "id") {
id = attr.value().toString();
} else if (attr.name() == "name") {
name = attr.value().toString();
}
}
if (id.isEmpty() || name.isEmpty() || type == olive::nodes::kInvalid) {
qCritical() << "Couldn't load field from" << em->filename << "- ID, type, and name cannot be empty.";
} else {
EffectRow* field = nullptr;
if (id.isEmpty() || name.isEmpty() || type == olive::nodes::kInvalid) {
qCritical() << "Couldn't load field from" << em->filename << "- ID, type, and name cannot be empty.";
} else {
EffectRow* field = nullptr;
switch (type) {
case olive::nodes::kFloat:
{
switch (type) {
case olive::nodes::kFloat:
{
DoubleInput* double_field = new DoubleInput(this, id, name);
DoubleInput* double_field = new DoubleInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
double_field->SetDefault(attr.value().toDouble());
} else if (attr.name() == "min") {
double_field->SetMinimum(attr.value().toDouble());
} else if (attr.name() == "max") {
double_field->SetMaximum(attr.value().toDouble());
}
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
double_field->SetDefault(attr.value().toDouble());
} else if (attr.name() == "min") {
double_field->SetMinimum(attr.value().toDouble());
} else if (attr.name() == "max") {
double_field->SetMaximum(attr.value().toDouble());
}
field = double_field;
}
break;
case olive::nodes::kColor:
{
QColor color;
field = new ColorInput(this, id, name);
field = double_field;
}
break;
case olive::nodes::kColor:
{
QColor color;
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "r") {
color.setRed(attr.value().toInt());
} else if (attr.name() == "g") {
color.setGreen(attr.value().toInt());
} else if (attr.name() == "b") {
color.setBlue(attr.value().toInt());
} else if (attr.name() == "rf") {
color.setRedF(attr.value().toDouble());
} else if (attr.name() == "gf") {
color.setGreenF(attr.value().toDouble());
} else if (attr.name() == "bf") {
color.setBlueF(attr.value().toDouble());
} else if (attr.name() == "hex") {
color.setNamedColor(attr.value().toString());
}
field = new ColorInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "r") {
color.setRed(attr.value().toInt());
} else if (attr.name() == "g") {
color.setGreen(attr.value().toInt());
} else if (attr.name() == "b") {
color.setBlue(attr.value().toInt());
} else if (attr.name() == "rf") {
color.setRedF(attr.value().toDouble());
} else if (attr.name() == "gf") {
color.setGreenF(attr.value().toDouble());
} else if (attr.name() == "bf") {
color.setBlueF(attr.value().toDouble());
} else if (attr.name() == "hex") {
color.setNamedColor(attr.value().toString());
}
field->SetValueAt(0, color);
}
break;
case olive::nodes::kString:
field = new StringInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
field->SetValueAt(0, attr.value().toString());
}
field->SetValueAt(0, color);
}
break;
case olive::nodes::kString:
field = new StringInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
field->SetValueAt(0, attr.value().toString());
}
break;
case olive::nodes::kBoolean:
field = new BoolInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
field->SetValueAt(0, attr.value() == "1");
}
}
break;
case olive::nodes::kBoolean:
field = new BoolInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
field->SetValueAt(0, attr.value() == "1");
}
break;
case olive::nodes::kCombo:
{
ComboInput* combo_field = new ComboInput(this, id, name);
int combo_default_index = 0;
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
combo_default_index = attr.value().toInt();
break;
}
}
break;
case olive::nodes::kCombo:
{
ComboInput* combo_field = new ComboInput(this, id, name);
int combo_default_index = 0;
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
combo_default_index = attr.value().toInt();
break;
}
int combo_item_count = 0;
while (!reader.atEnd() && !(reader.name() == "field" && reader.isEndElement())) {
}
int combo_item_count = 0;
while (!reader.atEnd() && !(reader.name() == "field" && reader.isEndElement())) {
reader.readNext();
if (reader.name() == "option" && reader.isStartElement()) {
reader.readNext();
if (reader.name() == "option" && reader.isStartElement()) {
reader.readNext();
combo_field->AddItem(reader.text().toString(), combo_item_count);
combo_item_count++;
}
combo_field->AddItem(reader.text().toString(), combo_item_count);
combo_item_count++;
}
combo_field->SetValueAt(0, combo_default_index);
field = combo_field;
}
break;
case olive::nodes::kFont:
field = new FontInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
field->SetValueAt(0, attr.value().toString());
}
}
break;
case olive::nodes::kFile:
field = new FileInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "filename") {
field->SetValueAt(0, attr.value().toString());
}
}
break;
}
combo_field->SetValueAt(0, combo_default_index);
field = combo_field;
}
} else if (reader.name() == "shader" && reader.isStartElement()) {
SetFlags(Flags() | ShaderFlag);
const QXmlStreamAttributes& attributes = reader.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "vert") {
shader_vert_path_ = attr.value().toString();
} else if (attr.name() == "frag") {
shader_frag_path_ = attr.value().toString();
} else if (attr.name() == "iterations") {
setIterations(attr.value().toInt());
} else if (attr.name() == "function") {
shader_function_name_ = attr.value().toString();
}
}
}/* else if (reader.name() == "superimpose" && reader.isStartElement()) {
enable_superimpose = true;
const QXmlStreamAttributes& attributes = reader.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "script") {
QFile script_file(get_effects_dir() + "/" + attr.value().toString());
if (script_file.open(QFile::ReadOnly)) {
script = script_file.readAll();
} else {
qCritical() << "Failed to open superimpose script file for" << em->filename;
enable_superimpose = false;
break;
case olive::nodes::kFont:
field = new FontInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "default") {
field->SetValueAt(0, attr.value().toString());
}
break;
}
break;
case olive::nodes::kFile:
field = new FileInput(this, id, name);
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "filename") {
field->SetValueAt(0, attr.value().toString());
}
}
break;
}
}*/
reader.readNext();
}
effect_file.close();
} else {
qCritical() << "Failed to open effect file" << em->filename;
}
} else if (reader.name() == "shader" && reader.isStartElement()) {
SetFlags(Flags() | ShaderFlag);
const QXmlStreamAttributes& attributes = reader.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "vert") {
shader_vert_path_ = attr.value().toString();
} else if (attr.name() == "frag") {
shader_frag_path_ = attr.value().toString();
} else if (attr.name() == "iterations") {
setIterations(attr.value().toInt());
} else if (attr.name() == "function") {
shader_function_name_ = attr.value().toString();
}
}
}/* else if (reader.name() == "superimpose" && reader.isStartElement()) {
enable_superimpose = true;
const QXmlStreamAttributes& attributes = reader.attributes();
for (int i=0;i<attributes.size();i++) {
const QXmlStreamAttribute& attr = attributes.at(i);
if (attr.name() == "script") {
QFile script_file(get_effects_dir() + "/" + attr.value().toString());
if (script_file.open(QFile::ReadOnly)) {
script = script_file.readAll();
} else {
qCritical() << "Failed to open superimpose script file for" << em->filename;
enable_superimpose = false;
}
break;
}
}
}*/
reader.readNext();
}
effect_file.close();
} else {
qCritical() << "Failed to open effect file" << em->filename;
}
}
}
+38 -29
View File
@@ -115,41 +115,50 @@ EffectUI::EffectUI(Effect* e) :
labels_.append(row_label);
layout_->addWidget(row_label, i, 0);
if (row->IsNodeOutput()) {
widgets_[i].resize(row->FieldCount());
QGridLayout* field_layout = new QGridLayout();
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
QWidget* widget = field->CreateWidget();
widgets_[i][j] = widget;
field_layout->addWidget(widget, 0, j);
}
layout_->addLayout(field_layout, i, 1);
KeyframeNavigator* nav;
if (row->IsKeyframable()) {
nav = new KeyframeNavigator();
nav->enable_keyframes(row->IsKeyframing());
AttachKeyframeNavigationToRow(row, nav);
layout_->addWidget(nav, i, 2);
row_label->setAlignment(Qt::AlignRight);
layout_->addWidget(row_label, i, 2);
} else {
nav = nullptr;
layout_->addWidget(row_label, i, 0);
widgets_[i].resize(row->FieldCount());
QGridLayout* field_layout = new QGridLayout();
for (int j=0;j<row->FieldCount();j++) {
EffectField* field = row->Field(j);
QWidget* widget = field->CreateWidget();
widgets_[i][j] = widget;
field_layout->addWidget(widget, 0, j);
}
layout_->addLayout(field_layout, i, 1);
KeyframeNavigator* nav;
if (row->IsKeyframable()) {
nav = new KeyframeNavigator();
nav->enable_keyframes(row->IsKeyframing());
AttachKeyframeNavigationToRow(row, nav);
layout_->addWidget(nav, i, 2);
} else {
nav = nullptr;
}
keyframe_navigators_[i] = nav;
}
keyframe_navigators_[i] = nav;
}
enabled_check->setChecked(e->IsEnabled());
+7 -6
View File
@@ -14,7 +14,7 @@
#include "ui/effectui.h"
const int kRoundedRectRadius = 5;
const int kNodePlugSize = 6;
const int kNodePlugSize = 10;
NodeUI::NodeUI() :
central_widget_(nullptr)
@@ -117,15 +117,16 @@ QVector<QRectF> NodeUI::GetNodeSocketRects()
Effect* e = central_widget_->GetEffect();
for (int i=0;i<e->row_count();i++) {
if (e->row(i)->CanConnectNodes()) {
int y = central_widget_->GetRowY(i);
rects.append(QRectF(rect().x(),
EffectRow* row = e->row(i);
qreal x = (row->IsNodeOutput()) ? rect().right() - kNodePlugSize : rect().x();
int y = central_widget_->GetRowY(i);
if (row->IsNodeInput() || row->IsNodeOutput()) {
rects.append(QRectF(x,
proxy_->pos().y() + y - kNodePlugSize/2,
kNodePlugSize,
kNodePlugSize));
}
}
}