Files
oak-editor/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp
T

533 lines
16 KiB
C++

#include "nodeparamviewwidgetbridge.h"
#include <QCheckBox>
#include <QFontComboBox>
#include <QLineEdit>
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
#include "core.h"
#include "node/node.h"
#include "nodeparamviewundo.h"
#include "project/item/sequence/sequence.h"
#include "undo/undostack.h"
#include "widget/footagecombobox/footagecombobox.h"
#include "widget/slider/floatslider.h"
#include "widget/slider/integerslider.h"
NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, QObject *parent) :
QObject(parent),
input_(input),
dragging_(false),
drag_created_keyframe_(false)
{
CreateWidgets();
connect(input_, &NodeInput::ValueChanged, this, &NodeParamViewWidgetBridge::InputValueChanged);
connect(input_, &NodeInput::PropertyChanged, this, &NodeParamViewWidgetBridge::PropertyChanged);
}
void NodeParamViewWidgetBridge::SetTime(const rational &time)
{
time_ = time;
if (!input_) {
return;
}
// We assume the first data type is the "primary" type
switch (input_->data_type()) {
// None of these inputs have applicable UI widgets
case NodeParam::kNone:
case NodeParam::kAny:
case NodeParam::kTexture:
case NodeParam::kMatrix:
case NodeParam::kRational:
case NodeParam::kSamples:
case NodeParam::kDecimal:
case NodeParam::kNumber:
case NodeParam::kString:
case NodeParam::kBuffer:
case NodeParam::kVector:
break;
case NodeParam::kInt:
static_cast<IntegerSlider*>(widgets_.first())->SetValue(input_->get_value_at_time(time).toLongLong());
break;
case NodeParam::kFloat:
static_cast<FloatSlider*>(widgets_.first())->SetValue(input_->get_value_at_time(time).toDouble());
break;
case NodeParam::kVec2:
{
QVector2D vec2 = input_->get_value_at_time(time).value<QVector2D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetValue(static_cast<double>(vec2.x()));
static_cast<FloatSlider*>(widgets_.at(1))->SetValue(static_cast<double>(vec2.y()));
break;
}
case NodeParam::kVec3:
{
QVector3D vec3 = input_->get_value_at_time(time).value<QVector3D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetValue(static_cast<double>(vec3.x()));
static_cast<FloatSlider*>(widgets_.at(1))->SetValue(static_cast<double>(vec3.y()));
static_cast<FloatSlider*>(widgets_.at(2))->SetValue(static_cast<double>(vec3.z()));
break;
}
case NodeParam::kVec4:
{
QVector4D vec4 = input_->get_value_at_time(time).value<QVector4D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetValue(static_cast<double>(vec4.x()));
static_cast<FloatSlider*>(widgets_.at(1))->SetValue(static_cast<double>(vec4.y()));
static_cast<FloatSlider*>(widgets_.at(2))->SetValue(static_cast<double>(vec4.z()));
static_cast<FloatSlider*>(widgets_.at(3))->SetValue(static_cast<double>(vec4.w()));
break;
}
case NodeParam::kFile:
// FIXME: File selector
break;
case NodeParam::kColor:
// FIXME: Color selector
break;
case NodeParam::kText:
{
static_cast<QLineEdit*>(widgets_.first())->setText(input_->get_value_at_time(time).toString());
break;
}
case NodeParam::kBoolean:
static_cast<QCheckBox*>(widgets_.first())->setChecked(input_->get_value_at_time(time).toBool());
break;
case NodeParam::kFont:
{
// FIXME: Implement this
break;
}
case NodeParam::kFootage:
static_cast<FootageComboBox*>(widgets_.first())->SetFootage(input_->get_value_at_time(time).value<StreamPtr>());
break;
}
}
const QList<QWidget *> &NodeParamViewWidgetBridge::widgets()
{
return widgets_;
}
void NodeParamViewWidgetBridge::CreateWidgets()
{
// We assume the first data type is the "primary" type
switch (input_->data_type()) {
// None of these inputs have applicable UI widgets
case NodeParam::kNone:
case NodeParam::kAny:
case NodeParam::kTexture:
case NodeParam::kMatrix:
case NodeParam::kRational:
case NodeParam::kSamples:
case NodeParam::kDecimal:
case NodeParam::kNumber:
case NodeParam::kString:
case NodeParam::kBuffer:
case NodeParam::kVector:
break;
case NodeParam::kInt:
{
IntegerSlider* slider = new IntegerSlider();
widgets_.append(slider);
connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
break;
}
case NodeParam::kFloat:
{
CreateSliders(1);
break;
}
case NodeParam::kVec2:
{
CreateSliders(2);
break;
}
case NodeParam::kVec3:
{
CreateSliders(3);
break;
}
case NodeParam::kVec4:
{
CreateSliders(4);
break;
}
case NodeParam::kFile:
// FIXME: File selector
break;
case NodeParam::kColor:
// FIXME: Color selector
break;
case NodeParam::kText:
{
QLineEdit* line_edit = new QLineEdit();
widgets_.append(line_edit);
connect(line_edit, &QLineEdit::textEdited, this, &NodeParamViewWidgetBridge::WidgetCallback);
break;
}
case NodeParam::kBoolean:
{
QCheckBox* check_box = new QCheckBox();
widgets_.append(check_box);
connect(check_box, &QCheckBox::clicked, this, &NodeParamViewWidgetBridge::WidgetCallback);
break;
}
case NodeParam::kFont:
{
QFontComboBox* font_combobox = new QFontComboBox();
widgets_.append(font_combobox);
break;
}
case NodeParam::kFootage:
{
FootageComboBox* footage_combobox = new FootageComboBox();
footage_combobox->SetRoot(static_cast<Sequence*>(input_->parentNode()->parent())->project()->root());
connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
widgets_.append(footage_combobox);
break;
}
}
// Check all properties
QHash<QString, QVariant>::const_iterator iterator;
for (iterator=input_->properties().begin();iterator!=input_->properties().end();iterator++) {
PropertyChanged(iterator.key(), iterator.value());
}
}
void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value, int track)
{
QUndoCommand* command = new QUndoCommand();
if (input_->is_keyframing()) {
NodeKeyframePtr existing_key = input_->get_keyframe_at_time_on_track(time_, track);
if (existing_key) {
new NodeParamSetKeyframeValueCommand(existing_key, value, command);
} else {
// No existing key, create a new one
NodeKeyframePtr new_key = NodeKeyframe::Create(time_,
value,
input_->get_best_keyframe_type_for_time(time_, track),
track);
new NodeParamInsertKeyframeCommand(input_, new_key, command);
}
} else {
new NodeParamSetStandardValueCommand(input_, track, value, command);
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
}
void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant &value)
{
int slider_track = widgets_.indexOf(slider);
if (slider->IsDragging()) {
// While we're dragging, we block the input's normal signalling and create our own
input_->blockSignals(true);
if (!dragging_) {
// Set up new drag
dragging_ = true;
// Cache current value
drag_old_value_ = input_->get_value_at_time_for_track(time_, slider_track);
// Determine whether we are creating a keyframe or not
if (input_->is_keyframing()) {
dragging_keyframe_ = input_->get_keyframe_at_time_on_track(time_, slider_track);
drag_created_keyframe_ = !dragging_keyframe_;
if (drag_created_keyframe_) {
dragging_keyframe_ = NodeKeyframe::Create(time_,
value,
input_->get_best_keyframe_type_for_time(time_, slider_track),
slider_track);
input_->insert_keyframe(dragging_keyframe_);
// We re-enable signals temporarily to emit the keyframe added signal
input_->blockSignals(false);
emit input_->KeyframeAdded(dragging_keyframe_);
input_->blockSignals(true);
}
}
}
if (input_->is_keyframing()) {
dragging_keyframe_->set_value(value);
} else {
input_->set_standard_value(value, slider_track);
}
input_->blockSignals(false);
input_->parentNode()->InvalidateVisible(input_);
} else {
if (dragging_) {
// We were dragging and just stopped
dragging_ = false;
QUndoCommand* command = new QUndoCommand();
if (input_->is_keyframing()) {
if (drag_created_keyframe_) {
// We created a keyframe in this process
new NodeParamInsertKeyframeCommand(input_, dragging_keyframe_, true, command);
}
// We just set a keyframe's value
// We do this even when inserting a keyframe because we don't actually perform an insert in this undo command
// so this will ensure the ValueChanged() signal is sent correctly
new NodeParamSetKeyframeValueCommand(dragging_keyframe_, value, drag_old_value_, command);
} else {
// We just set the standard value
new NodeParamSetStandardValueCommand(input_, slider_track, value, drag_old_value_, command);
}
Core::instance()->undo_stack()->push(command);
} else {
// No drag was involved, we can just push the value
SetInputValue(value, slider_track);
}
}
}
void NodeParamViewWidgetBridge::WidgetCallback()
{
switch (input_->data_type()) {
// None of these inputs have applicable UI widgets
case NodeParam::kNone:
case NodeParam::kAny:
case NodeParam::kTexture:
case NodeParam::kMatrix:
case NodeParam::kSamples:
case NodeParam::kRational:
case NodeParam::kDecimal:
case NodeParam::kNumber:
case NodeParam::kString:
case NodeParam::kVector:
case NodeParam::kBuffer:
break;
case NodeParam::kInt:
{
// Widget is a IntegerSlider
IntegerSlider* slider = static_cast<IntegerSlider*>(sender());
ProcessSlider(slider, QVariant::fromValue(slider->GetValue()));
break;
}
case NodeParam::kFloat:
{
// Widget is a FloatSlider
FloatSlider* slider = static_cast<FloatSlider*>(sender());
ProcessSlider(slider, slider->GetValue());
break;
}
case NodeParam::kVec2:
{
// Widget is a FloatSlider
FloatSlider* slider = static_cast<FloatSlider*>(sender());
ProcessSlider(slider, slider->GetValue());
break;
}
case NodeParam::kVec3:
{
// Widget is a FloatSlider
FloatSlider* slider = static_cast<FloatSlider*>(sender());
ProcessSlider(slider, slider->GetValue());
break;
}
case NodeParam::kVec4:
{
// Widget is a FloatSlider
FloatSlider* slider = static_cast<FloatSlider*>(sender());
ProcessSlider(slider, slider->GetValue());
break;
}
case NodeParam::kFile:
// FIXME: File selector
break;
case NodeParam::kColor:
// FIXME: Color selector
break;
case NodeParam::kText:
{
// Sender is a QLineEdit
SetInputValue(static_cast<QLineEdit*>(sender())->text(), 0);
break;
}
case NodeParam::kBoolean:
{
// Widget is a QCheckBox
SetInputValue(static_cast<QCheckBox*>(sender())->isChecked(), 0);
break;
}
case NodeParam::kFont:
{
// Widget is a QFontComboBox
SetInputValue(static_cast<QFontComboBox*>(sender())->currentFont(), 0);
break;
}
case NodeParam::kFootage:
{
// Widget is a FootageComboBox
SetInputValue(QVariant::fromValue(static_cast<FootageComboBox*>(sender())->SelectedFootage()), 0);
break;
}
}
}
void NodeParamViewWidgetBridge::CreateSliders(int count)
{
for (int i=0;i<count;i++) {
FloatSlider* fs = new FloatSlider();
widgets_.append(fs);
connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
}
}
void NodeParamViewWidgetBridge::InputValueChanged(const rational &start, const rational &end)
{
if (!dragging_ && start <= time_ && end >= time_) {
// We'll need to update the widgets because the values have changed on our current time
SetTime(time_);
}
}
void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVariant &value)
{
// Parameters for vectors only
if (input_->data_type() & NodeParam::kVector) {
if (key == QStringLiteral("disablex")) {
static_cast<FloatSlider*>(widgets_.at(0))->setEnabled(!value.toBool());
} else if (key == QStringLiteral("disabley")) {
static_cast<FloatSlider*>(widgets_.at(1))->setEnabled(!value.toBool());
} else if (widgets_.size() > 2 && key == QStringLiteral("disablez")) {
static_cast<FloatSlider*>(widgets_.at(2))->setEnabled(!value.toBool());
} else if (widgets_.size() > 3 && key == QStringLiteral("disablew")) {
static_cast<FloatSlider*>(widgets_.at(3))->setEnabled(!value.toBool());
}
}
// Parameters for integers, floats, and vectors
if (input_->data_type() & NodeParam::kNumber || input_->data_type() & NodeParam::kVector) {
if (key == QStringLiteral("min")) {
switch (input_->data_type()) {
case NodeParam::kInt:
static_cast<IntegerSlider*>(widgets_.first())->SetMinimum(value.value<int64_t>());
break;
case NodeParam::kFloat:
static_cast<FloatSlider*>(widgets_.first())->SetMinimum(value.toDouble());
break;
case NodeParam::kRational:
// FIXME: Rational doesn't have a UI implementation yet
break;
case NodeParam::kVec2:
{
QVector2D min = value.value<QVector2D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetMinimum(min.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetMinimum(min.y());
break;
}
case NodeParam::kVec3:
{
QVector3D min = value.value<QVector3D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetMinimum(min.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetMinimum(min.y());
static_cast<FloatSlider*>(widgets_.at(2))->SetMinimum(min.z());
break;
}
case NodeParam::kVec4:
{
QVector4D min = value.value<QVector4D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetMinimum(min.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetMinimum(min.y());
static_cast<FloatSlider*>(widgets_.at(2))->SetMinimum(min.z());
static_cast<FloatSlider*>(widgets_.at(3))->SetMinimum(min.w());
break;
}
default:
break;
}
} else if (key == QStringLiteral("max")) {
switch (input_->data_type()) {
case NodeParam::kInt:
static_cast<IntegerSlider*>(widgets_.first())->SetMaximum(value.value<int64_t>());
break;
case NodeParam::kFloat:
static_cast<FloatSlider*>(widgets_.first())->SetMaximum(value.toDouble());
break;
case NodeParam::kRational:
// FIXME: Rational doesn't have a UI implementation yet
break;
case NodeParam::kVec2:
{
QVector2D max = value.value<QVector2D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetMaximum(max.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetMaximum(max.y());
break;
}
case NodeParam::kVec3:
{
QVector3D max = value.value<QVector3D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetMaximum(max.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetMaximum(max.y());
static_cast<FloatSlider*>(widgets_.at(2))->SetMaximum(max.z());
break;
}
case NodeParam::kVec4:
{
QVector4D max = value.value<QVector4D>();
static_cast<FloatSlider*>(widgets_.at(0))->SetMaximum(max.x());
static_cast<FloatSlider*>(widgets_.at(1))->SetMaximum(max.y());
static_cast<FloatSlider*>(widgets_.at(2))->SetMaximum(max.z());
static_cast<FloatSlider*>(widgets_.at(3))->SetMaximum(max.w());
break;
}
default:
break;
}
}
}
// Parameters for floats and vectors only
if (input_->data_type() & NodeParam::kFloat || input_->data_type() & NodeParam::kVector) {
if (key == QStringLiteral("view")) {
FloatSlider::DisplayType display_type;
if (value == QStringLiteral("percent")) {
display_type = FloatSlider::kPercentage;
} else if (value == QStringLiteral("db")) {
display_type = FloatSlider::kDecibel;
} else {
// Avoid undefined behavior
return;
}
foreach (QWidget* w, widgets_) {
static_cast<FloatSlider*>(w)->SetDisplayType(display_type);
}
}
}
}