implemented base UI keyframe functionality

This commit implements much of the base functionality for creating and
destroying keyframes in the UI. Not complete yet, but getting there.
This commit is contained in:
itsmattkc
2019-12-26 16:32:08 +11:00
parent a514265fc9
commit 23001386d0
16 changed files with 707 additions and 301 deletions
+26
View File
@@ -10,6 +10,23 @@ KeyframeView::KeyframeView(QWidget *parent) :
setDragMode(QGraphicsView::RubberBandDrag);
}
void KeyframeView::AddKeyframe(NodeKeyframePtr key, int y)
{
QPoint global_pt(0, y);
QPoint local_pt = mapFromGlobal(global_pt);
QPointF scene_pt = mapToScene(local_pt);
KeyframeViewItem* item = new KeyframeViewItem(key, scene_pt.y());
item->SetScale(scale_);
item_map_.insert(key.get(), item);
scene()->addItem(item);
}
void KeyframeView::RemoveKeyframe(NodeKeyframePtr key)
{
delete item_map_.value(key.get());
}
void KeyframeView::mousePressEvent(QMouseEvent *event)
{
if (PlayheadPress(event)) {
@@ -36,3 +53,12 @@ void KeyframeView::mouseReleaseEvent(QMouseEvent *event)
QGraphicsView::mouseReleaseEvent(event);
}
void KeyframeView::ScaleChangedEvent(double scale)
{
QMap<NodeKeyframe*, KeyframeViewItem*>::const_iterator iterator;
for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) {
iterator.value()->SetScale(scale);
}
}
+8 -2
View File
@@ -9,18 +9,24 @@
class KeyframeView : public TimelineViewBase
{
Q_OBJECT
public:
KeyframeView(QWidget* parent = nullptr);
void AddKeyframe(const rational& time, int y);
public slots:
void AddKeyframe(NodeKeyframePtr key, int y);
void RemoveKeyframe(NodeKeyframePtr key);
protected:
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
private:
virtual void ScaleChangedEvent(double scale) override;
private:
QMap<NodeKeyframe*, KeyframeViewItem*> item_map_;
};
#endif // KEYFRAMEVIEW_H
+5 -20
View File
@@ -7,25 +7,18 @@
#include "common/qtversionabstraction.h"
KeyframeViewItem::KeyframeViewItem(QGraphicsItem *parent) :
KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, qreal vcenter, QGraphicsItem *parent) :
QGraphicsRectItem(parent),
key_(nullptr),
scale_(1.0)
key_(key),
scale_(1.0),
middle_(vcenter)
{
keyframe_size_ = QFontMetricsWidth(qApp->fontMetrics(), "Oi");
setFlag(QGraphicsItem::ItemIsSelectable);
setFlag(QGraphicsItem::ItemIsMovable);
}
void KeyframeViewItem::SetKeyframe(NodeKeyframe *key)
{
key_ = key;
UpdateRect();
}
connect(key.get(), SIGNAL(TimeChanged(const rational&)), this, SLOT(UpdateRect()));
void KeyframeViewItem::SetVerticalCenter(int middle)
{
middle_ = middle;
UpdateRect();
}
@@ -37,10 +30,6 @@ void KeyframeViewItem::SetScale(double scale)
void KeyframeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
if (!key_) {
return;
}
painter->setPen(Qt::black);
if (option->state & QStyle::State_Selected) {
@@ -73,10 +62,6 @@ void KeyframeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
void KeyframeViewItem::UpdateRect()
{
if (!key_) {
return;
}
double x_center = key_->time().toDouble() * scale_;
setRect(x_center - keyframe_size_/2, middle_ - keyframe_size_/2, keyframe_size_, keyframe_size_);
+9 -10
View File
@@ -5,14 +5,11 @@
#include "node/keyframe.h"
class KeyframeViewItem : public QGraphicsRectItem
class KeyframeViewItem : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
KeyframeViewItem(QGraphicsItem *parent = nullptr);
void SetKeyframe(NodeKeyframe* key);
void SetVerticalCenter(int middle);
KeyframeViewItem(NodeKeyframePtr key, qreal vcenter, QGraphicsItem *parent = nullptr);
void SetScale(double scale);
@@ -20,15 +17,17 @@ protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
private:
void UpdateRect();
NodeKeyframe* key_;
NodeKeyframePtr key_;
double scale_;
int middle_;
qreal middle_;
int keyframe_size_;
private slots:
void UpdateRect();
};
#endif // KEYFRAMEVIEWITEM_H
+36 -21
View File
@@ -23,6 +23,7 @@
#include <QScrollArea>
#include <QSplitter>
#include "common/timecodefunctions.h"
#include "node/output/viewer/viewer.h"
NodeParamView::NodeParamView(QWidget *parent) :
@@ -70,6 +71,8 @@ NodeParamView::NodeParamView(QWidget *parent) :
// Connect ruler and keyframe view together
connect(ruler_, SIGNAL(TimeChanged(const int64_t&)), keyframe_view_, SLOT(SetTime(const int64_t&)));
connect(keyframe_view_, SIGNAL(TimeChanged(const int64_t&)), ruler_, SLOT(SetTime(const int64_t&)));
connect(ruler_, SIGNAL(TimeChanged(const int64_t&)), this, SLOT(RulerTimeChanged(const int64_t&)));
connect(keyframe_view_, SIGNAL(TimeChanged(const int64_t&)), this, SLOT(RulerTimeChanged(const int64_t&)));
splitter->addWidget(keyframe_area);
@@ -93,30 +96,16 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
// For each node, create a widget
foreach (Node* node, nodes_) {
NodeParamViewItem* item = new NodeParamViewItem(node);
// See if we already have an item for this node type
// Insert the widget before the stretch
param_layout_->insertWidget(param_layout_->count() - 1, item);
bool merged_node = false;
connect(item, SIGNAL(KeyframeAdded(NodeKeyframePtr, int)), keyframe_view_, SLOT(AddKeyframe(NodeKeyframePtr, int)));
connect(item, SIGNAL(KeyframeRemoved(NodeKeyframePtr)), keyframe_view_, SLOT(RemoveKeyframe(NodeKeyframePtr)));
connect(item, SIGNAL(RequestSetTime(const rational&)), this, SLOT(SetTime(const rational&)));
foreach (NodeParamViewItem* existing_item, items_) {
if (existing_item->CanAddNode(node)) {
existing_item->AttachNode(node);
merged_node = true;
break;
}
}
// If we couldn't merge this node into the existing item, create a new one
if (!merged_node) {
NodeParamViewItem* item = new NodeParamViewItem(this);
item->AttachNode(node);
// Insert the widget before the stretch
param_layout_->insertWidget(param_layout_->count() - 1, item);
items_.append(item);
}
items_.append(item);
}
if (!nodes_.isEmpty()) {
@@ -126,6 +115,8 @@ void NodeParamView::SetNodes(QList<Node *> nodes)
SetTimebase(viewer->video_params().time_base());
}
}
SetTime(0);
}
const QList<Node *> &NodeParamView::nodes()
@@ -144,8 +135,32 @@ void NodeParamView::SetScale(const double& scale)
keyframe_view_->SetScale(scale);
}
void NodeParamView::SetTime(const rational &time)
{
int64_t timestamp = olive::time_to_timestamp(time, keyframe_view_->timebase());
ruler_->SetTime(timestamp);
keyframe_view_->SetTime(timestamp);
UpdateItemTime(time);
}
void NodeParamView::SetTimebase(const rational &timebase)
{
ruler_->SetTimebase(timebase);
keyframe_view_->SetTimebase(timebase);
}
void NodeParamView::UpdateItemTime(const rational &time)
{
foreach (NodeParamViewItem* item, items_) {
item->SetTime(time);
}
}
void NodeParamView::RulerTimeChanged(const int64_t &timestamp)
{
rational time = olive::timestamp_to_time(timestamp, keyframe_view_->timebase());
UpdateItemTime(time);
}
+10
View File
@@ -31,6 +31,7 @@
class NodeParamView : public QWidget
{
Q_OBJECT
public:
NodeParamView(QWidget* parent);
@@ -40,9 +41,14 @@ public:
const double& GetScale() const;
void SetScale(const double &scale);
public slots:
void SetTime(const rational& time);
private:
void SetTimebase(const rational& timebase);
void UpdateItemTime(const rational& time);
QVBoxLayout* param_layout_;
KeyframeView* keyframe_view_;
@@ -52,6 +58,10 @@ private:
QList<Node*> nodes_;
QList<NodeParamViewItem*> items_;
private slots:
void RulerTimeChanged(const int64_t& timestamp);
};
#endif // NODEPARAMVIEW_H
+163 -58
View File
@@ -26,14 +26,14 @@
#include <QMessageBox>
#include <QPainter>
#include "nodeparamviewkeyframecontrol.h"
#include "nodeparamviewundo.h"
#include "project/item/sequence/sequence.h"
#include "ui/icons/icons.h"
#include "undo/undostack.h"
NodeParamViewItem::NodeParamViewItem(QWidget *parent) :
QWidget(parent)
NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) :
QWidget(parent),
node_(node)
{
QVBoxLayout* main_layout = new QVBoxLayout(this);
main_layout->setSpacing(0);
@@ -70,48 +70,52 @@ NodeParamViewItem::NodeParamViewItem(QWidget *parent) :
// Set correct widget state
SetExpanded(title_bar_collapse_btn_->isChecked());
SetupUI();
}
void NodeParamViewItem::AttachNode(Node *n)
void NodeParamViewItem::SetTime(const rational &time)
{
// Make sure we can attach this node (CanAddNode() should be run by the caller to make sure this node is valid)
Q_ASSERT(CanAddNode(n));
time_ = time;
// Add node to the list
nodes_.append(n);
// If the added node was the first node, set up the UI
if (nodes_.size() == 1) {
SetupUI();
} else {
AddAdditionalNode(n);
foreach (NodeParamViewWidgetBridge* bridge, bridges_) {
// Updates the UI widgets from the time
bridge->SetTime(time_);
}
}
bool NodeParamViewItem::CanAddNode(Node* n)
{
// Ensures that all Nodes have the same ID
return (nodes_.isEmpty() || nodes_.first()->id() == n->id());
foreach (NodeParamViewKeyframeControl* key_control, key_control_list_) {
UpdateKeyframeControl(key_control);
}
}
void NodeParamViewItem::changeEvent(QEvent *e)
{
if (e->type() == QEvent::LanguageChange && !nodes_.isEmpty()) {
if (e->type() == QEvent::LanguageChange) {
Retranslate();
}
QWidget::changeEvent(e);
}
void NodeParamViewItem::InputAddedKeyframeInternal(NodeInput *input, NodeKeyframePtr keyframe)
{
// Find its row in the parameters
QLabel* lbl = label_map_.value(input);
// Find label's Y position
QPoint lbl_center = lbl->rect().center();
// Find global position
lbl_center = lbl->mapToGlobal(lbl_center);
emit KeyframeAdded(keyframe, lbl_center.y());
}
void NodeParamViewItem::SetupUI()
{
Q_ASSERT(!nodes_.isEmpty());
Node* first_node = nodes_.first();
int row_count = 0;
foreach (NodeParam* param, first_node->parameters()) {
foreach (NodeParam* param, node_->parameters()) {
// This widget only needs to show input parameters
if (param->type() == NodeParam::kInput) {
NodeInput* input = static_cast<NodeInput*>(param);
@@ -120,11 +124,12 @@ void NodeParamViewItem::SetupUI()
QLabel* param_label = new QLabel();
param_lbls_.append(param_label);
label_map_.insert(input, param_label);
content_layout_->addWidget(param_label, row_count, 0);
// Create a widget/input bridge for this input
NodeParamViewWidgetBridge* bridge = new NodeParamViewWidgetBridge(this);
bridge->AddInput(input);
NodeParamViewWidgetBridge* bridge = new NodeParamViewWidgetBridge(input, this);
bridges_.append(bridge);
// Add widgets for this parameter ot the layout
@@ -140,6 +145,15 @@ void NodeParamViewItem::SetupUI()
NodeParamViewKeyframeControl* key_control = new NodeParamViewKeyframeControl(input);
content_layout_->addWidget(key_control, row_count, control_column);
connect(key_control, &NodeParamViewKeyframeControl::KeyframeEnableChanged, this, &NodeParamViewItem::UserChangedKeyframeEnable);
connect(key_control, &NodeParamViewKeyframeControl::GoToPreviousKey, this, &NodeParamViewItem::GoToPreviousKey);
connect(key_control, &NodeParamViewKeyframeControl::GoToNextKey, this, &NodeParamViewItem::GoToNextKey);
connect(key_control, &NodeParamViewKeyframeControl::KeyframeToggled, this, &NodeParamViewItem::UserToggledKeyframe);
key_control_list_.append(key_control);
connect(input, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewItem::InputKeyframeEnableChanged);
connect(input, &NodeInput::KeyframeAdded, this, &NodeParamViewItem::InputAddedKeyframe);
connect(input, &NodeInput::KeyframeRemoved, this, &NodeParamViewItem::KeyframeRemoved);
}
row_count++;
@@ -149,30 +163,15 @@ void NodeParamViewItem::SetupUI()
Retranslate();
}
void NodeParamViewItem::AddAdditionalNode(Node *n)
{
int bridge_count = 0;
foreach (NodeParam* param, n->parameters()) {
if (param->type() == NodeParam::kInput) {
bridges_.at(bridge_count)->AddInput(static_cast<NodeInput*>(param));
bridge_count++;
}
}
}
void NodeParamViewItem::Retranslate()
{
Node* first_node = nodes_.first();
node_->Retranslate();
first_node->Retranslate();
title_bar_lbl_->setText(first_node->Name());
title_bar_lbl_->setText(node_->Name());
int row_count = 0;
foreach (NodeParam* param, first_node->parameters()) {
foreach (NodeParam* param, node_->parameters()) {
// This widget only needs to show input parameters
if (param->type() == NodeParam::kInput) {
param_lbls_.at(row_count)->setText(tr("%1:").arg(param->name()));
@@ -182,6 +181,27 @@ void NodeParamViewItem::Retranslate()
}
}
void NodeParamViewItem::UpdateKeyframeControl(NodeParamViewKeyframeControl *key_control)
{
NodeInput* input = key_control->GetConnectedInput();
// Update UI based on time
key_control->SetPreviousButtonEnabled(time_ > input->keyframes().first()->time());
key_control->SetNextButtonEnabled(time_ < input->keyframes().last()->time());
key_control->SetToggleButtonChecked(input->has_keyframe_at_time(time_));
}
NodeParamViewKeyframeControl *NodeParamViewItem::KeyframeControlFromInput(NodeInput *input) const
{
foreach (NodeParamViewKeyframeControl* key_control, key_control_list_) {
if (key_control->GetConnectedInput() == input) {
return key_control;
}
}
return nullptr;
}
void NodeParamViewItem::SetExpanded(bool e)
{
expanded_ = e;
@@ -195,7 +215,7 @@ void NodeParamViewItem::SetExpanded(bool e)
}
}
void NodeParamViewItem::KeyframeEnableChanged(bool e)
void NodeParamViewItem::UserChangedKeyframeEnable(bool e)
{
NodeParamViewKeyframeControl* control = static_cast<NodeParamViewKeyframeControl*>(sender());
NodeInput* input = control->GetConnectedInput();
@@ -205,15 +225,14 @@ void NodeParamViewItem::KeyframeEnableChanged(bool e)
return;
}
QUndoCommand* command = new QUndoCommand();
if (e) {
QUndoCommand* command = new QUndoCommand();
// Enable keyframing
new NodeParamSetKeyframing(input, true, command);
new NodeParamSetKeyframingCommand(input, true, command);
// FIXME: Create a keyframe at this time
olive::undo_stack.push(command);
// NodeInputs already have one keyframe by default, we move it to the current time here
new NodeParamSetKeyframeTimeCommand(input->keyframes().first(), time_, command);
} else {
// Confirm the user wants to clear all keyframes
if (QMessageBox::warning(this,
@@ -221,21 +240,107 @@ void NodeParamViewItem::KeyframeEnableChanged(bool e)
tr("Are you sure you want to disable keyframing on this value? This will clear all existing keyframes."),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
// Disable keyframing
QUndoCommand* command = new QUndoCommand();
// Store value at this time, we'll set this as the persistent value later
QVariant stored_val = input->get_value_at_time(time_);
// FIXME: Delete all keyframes
// Delete all keyframes EXCEPT ONE
for (int i=input->keyframes().size()-1;i>0;i--) {
new NodeParamRemoveKeyframeCommand(input, input->keyframes().at(i), command);
}
// Update value with this one
new NodeParamSetKeyframeValueCommand(input->keyframes().first(), stored_val, command);
// Disable keyframing
new NodeParamSetKeyframing(input, false, command);
olive::undo_stack.push(command);
new NodeParamSetKeyframingCommand(input, false, command);
} else {
// Disable action has effectively been ignored
control->SetKeyframeEnabled(true);
}
}
olive::undo_stack.pushIfHasChildren(command);
}
void NodeParamViewItem::UserToggledKeyframe(bool e)
{
NodeParamViewKeyframeControl* control = static_cast<NodeParamViewKeyframeControl*>(sender());
NodeInput* input = control->GetConnectedInput();
NodeKeyframePtr key = input->get_keyframe_at_time(time_);
QUndoCommand* command = new QUndoCommand();
if (e && !key) {
// Add a keyframe here
NodeKeyframePtr closest_key = input->get_closest_keyframe_to_time(time_);
key = std::make_shared<NodeKeyframe>(time_, input->get_value_at_time(time_), closest_key->type());
new NodeParamInsertKeyframeCommand(input, key, command);
} else if (!e && key) {
// Remove a keyframe here
new NodeParamRemoveKeyframeCommand(input, key, command);
}
olive::undo_stack.pushIfHasChildren(command);
}
void NodeParamViewItem::InputKeyframeEnableChanged(bool e)
{
NodeInput* input = static_cast<NodeInput*>(sender());
foreach (NodeKeyframePtr key, input->keyframes()) {
if (e) {
// Add a keyframe item for each keyframe
InputAddedKeyframeInternal(input, key);
} else {
// Remove each keyframe item
emit KeyframeRemoved(key);
}
}
}
void NodeParamViewItem::InputAddedKeyframe(NodeKeyframePtr key)
{
// Get NodeInput that emitted this signal
NodeInput* input = static_cast<NodeInput*>(sender());
InputAddedKeyframeInternal(input, key);
UpdateKeyframeControl(KeyframeControlFromInput(input));
}
void NodeParamViewItem::GoToPreviousKey()
{
NodeParamViewKeyframeControl* key_control = static_cast<NodeParamViewKeyframeControl*>(sender());
NodeInput* input = key_control->GetConnectedInput();
for (int i=input->keyframes().size()-1;i>=0;i--) {
// Find closest keyframe that is before this time
const rational& this_key_time = input->keyframes().at(i)->time();
if (this_key_time < time_) {
emit RequestSetTime(this_key_time);
break;
}
}
}
void NodeParamViewItem::GoToNextKey()
{
NodeParamViewKeyframeControl* key_control = static_cast<NodeParamViewKeyframeControl*>(sender());
NodeInput* input = key_control->GetConnectedInput();
for (int i=0;i<input->keyframes().size();i++) {
// Find closest keyframe that is before this time
const rational& this_key_time = input->keyframes().at(i)->time();
if (this_key_time > time_) {
emit RequestSetTime(this_key_time);
break;
}
}
}
NodeParamViewItemTitleBar::NodeParamViewItemTitleBar(QWidget *parent) :
+35 -8
View File
@@ -28,6 +28,7 @@
#include <QWidget>
#include "node/node.h"
#include "nodeparamviewkeyframecontrol.h"
#include "nodeparamviewwidgetbridge.h"
class NodeParamViewItemTitleBar : public QWidget {
@@ -42,22 +43,31 @@ class NodeParamViewItem : public QWidget
{
Q_OBJECT
public:
NodeParamViewItem(QWidget* parent);
NodeParamViewItem(Node* node, QWidget* parent = nullptr);
void AttachNode(Node* n);
void SetTime(const rational& time);
bool CanAddNode(Node *n);
signals:
void KeyframeAdded(NodeKeyframePtr key, int y);
void KeyframeRemoved(NodeKeyframePtr key);
void RequestSetTime(const rational& time);
protected:
void changeEvent(QEvent *e) override;
virtual void changeEvent(QEvent *e) override;
private:
void InputAddedKeyframeInternal(NodeInput* input, NodeKeyframePtr keyframe);
void SetupUI();
void AddAdditionalNode(Node* n);
void Retranslate();
void UpdateKeyframeControl(NodeParamViewKeyframeControl* key_control);
NodeParamViewKeyframeControl* KeyframeControlFromInput(NodeInput* input) const;
bool expanded_;
NodeParamViewItemTitleBar* title_bar_;
@@ -72,14 +82,31 @@ private:
QGridLayout* content_layout_;
QList<Node*> nodes_;
Node* node_;
QList<NodeParamViewWidgetBridge*> bridges_;
rational time_;
QMap<NodeInput*, QLabel*> label_map_;
QList<NodeParamViewKeyframeControl*> key_control_list_;
private slots:
void SetExpanded(bool e);
void KeyframeEnableChanged(bool e);
void UserChangedKeyframeEnable(bool e);
void UserToggledKeyframe(bool e);
void InputKeyframeEnableChanged(bool e);
void InputAddedKeyframe(NodeKeyframePtr key);
void GoToPreviousKey();
void GoToNextKey();
};
#endif // NODEPARAMVIEWITEM_H
@@ -33,8 +33,12 @@ NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(NodeInput* input, QWi
enable_key_btn_->setIconSize(enable_key_btn_->iconSize() / 4 * 3);
layout->addWidget(enable_key_btn_);
connect(enable_key_btn_, SIGNAL(toggled(bool)), this, SLOT(ShowButtonsFromKeyframeEnable(bool)));
connect(enable_key_btn_, SIGNAL(toggled(bool)), this, SIGNAL(KeyframeEnableChanged(bool)));
connect(prev_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::GoToPreviousKey);
connect(next_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::GoToNextKey);
connect(toggle_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::KeyframeToggled);
connect(enable_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable);
connect(enable_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged);
connect(input_, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewKeyframeControl::SetKeyframeEnabled);
// Pick up keyframing value
ShowButtonsFromKeyframeEnable(input_->is_keyframing());
@@ -45,6 +49,26 @@ NodeInput *NodeParamViewKeyframeControl::GetConnectedInput() const
return input_;
}
void NodeParamViewKeyframeControl::SetPreviousButtonEnabled(bool enabled)
{
prev_key_btn_->setEnabled(enabled);
}
void NodeParamViewKeyframeControl::SetNextButtonEnabled(bool enabled)
{
next_key_btn_->setEnabled(enabled);
}
void NodeParamViewKeyframeControl::SetToggleButtonChecked(bool checked)
{
// Suppress KeyframeToggled() signal from this object
blockSignals(true);
toggle_key_btn_->setChecked(checked);
blockSignals(false);
}
void NodeParamViewKeyframeControl::SetKeyframeEnabled(bool e)
{
// Suppress KeyframeEnableChanged() signal from this object
@@ -14,12 +14,21 @@ public:
NodeInput* GetConnectedInput() const;
void SetPreviousButtonEnabled(bool enabled);
void SetNextButtonEnabled(bool enabled);
void SetToggleButtonChecked(bool checked);
public slots:
void SetKeyframeEnabled(bool e);
signals:
void KeyframeEnableChanged(bool);
void GoToPreviousKey();
void GoToNextKey();
void KeyframeToggled(bool);
private:
QPushButton* CreateNewToolButton(const QIcon &icon) const;
+73 -3
View File
@@ -1,6 +1,6 @@
#include "nodeparamviewundo.h"
NodeParamSetKeyframing::NodeParamSetKeyframing(NodeInput *input, bool setting, QUndoCommand *parent) :
NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, bool setting, QUndoCommand *parent) :
QUndoCommand(parent),
input_(input),
setting_(setting)
@@ -8,12 +8,82 @@ NodeParamSetKeyframing::NodeParamSetKeyframing(NodeInput *input, bool setting, Q
Q_ASSERT(setting != input_->is_keyframing());
}
void NodeParamSetKeyframing::redo()
void NodeParamSetKeyframingCommand::redo()
{
input_->set_is_keyframing(setting_);
}
void NodeParamSetKeyframing::undo()
void NodeParamSetKeyframingCommand::undo()
{
input_->set_is_keyframing(!setting_);
}
NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent) :
QUndoCommand(parent),
key_(key),
old_value_(key_->value()),
new_value_(value)
{
}
void NodeParamSetKeyframeValueCommand::redo()
{
key_->set_value(new_value_);
}
void NodeParamSetKeyframeValueCommand::undo()
{
key_->set_value(old_value_);
}
NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand* parent) :
QUndoCommand(parent),
input_(input),
keyframe_(keyframe)
{
}
void NodeParamInsertKeyframeCommand::redo()
{
input_->insert_keyframe(keyframe_);
}
void NodeParamInsertKeyframeCommand::undo()
{
input_->remove_keyframe(keyframe_);
}
NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand *parent) :
QUndoCommand(parent),
input_(input),
keyframe_(keyframe)
{
}
void NodeParamRemoveKeyframeCommand::redo()
{
input_->remove_keyframe(keyframe_);
}
void NodeParamRemoveKeyframeCommand::undo()
{
input_->insert_keyframe(keyframe_);
}
NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational &time, QUndoCommand *parent) :
QUndoCommand(parent),
key_(key),
old_time_(key->time()),
new_time_(time)
{
}
void NodeParamSetKeyframeTimeCommand::redo()
{
key_->set_time(new_time_);
}
void NodeParamSetKeyframeTimeCommand::undo()
{
key_->set_time(old_time_);
}
+60 -2
View File
@@ -5,9 +5,9 @@
#include "node/input.h"
class NodeParamSetKeyframing : public QUndoCommand {
class NodeParamSetKeyframingCommand : public QUndoCommand {
public:
NodeParamSetKeyframing(NodeInput* input, bool setting, QUndoCommand* parent = nullptr);
NodeParamSetKeyframingCommand(NodeInput* input, bool setting, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
@@ -17,4 +17,62 @@ private:
bool setting_;
};
class NodeParamInsertKeyframeCommand : public QUndoCommand {
public:
NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
NodeInput* input_;
NodeKeyframePtr keyframe_;
};
class NodeParamRemoveKeyframeCommand : public QUndoCommand {
public:
NodeParamRemoveKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
NodeInput* input_;
NodeKeyframePtr keyframe_;
};
class NodeParamSetKeyframeTimeCommand : public QUndoCommand {
public:
NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational& time, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
NodeKeyframePtr key_;
rational old_time_;
rational new_time_;
};
class NodeParamSetKeyframeValueCommand : public QUndoCommand {
public:
NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent = nullptr);
virtual void redo() override;
virtual void undo() override;
private:
NodeKeyframePtr key_;
QVariant old_value_;
QVariant new_value_;
};
#endif // NODEPARAMVIEWUNDO_H
@@ -8,27 +8,95 @@
#include <QVector4D>
#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"
// FIXME: Test code only
#include "panel/panelmanager.h"
#include "panel/project/project.h"
// End test code
NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(QObject* parent) :
QObject(parent)
NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, QObject *parent) :
QObject(parent),
input_(input)
{
CreateWidgets();
}
void NodeParamViewWidgetBridge::AddInput(NodeInput *input)
void NodeParamViewWidgetBridge::SetTime(const rational &time)
{
inputs_.append(input);
time_ = time;
// If this was the first input, create the widgets
if (inputs_.size() == 1) {
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::kWholeNumber:
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;
}
}
@@ -39,10 +107,8 @@ const QList<QWidget *> &NodeParamViewWidgetBridge::widgets()
void NodeParamViewWidgetBridge::CreateWidgets()
{
NodeInput* base_input = inputs_.first();
// We assume the first data type is the "primary" type
switch (base_input->data_type()) {
switch (input_->data_type()) {
// None of these inputs have applicable UI widgets
case NodeParam::kNone:
case NodeParam::kAny:
@@ -61,14 +127,12 @@ void NodeParamViewWidgetBridge::CreateWidgets()
{
IntegerSlider* slider = new IntegerSlider();
slider->SetValue(base_input->get_value_at_time(0).toLongLong());
if (base_input->has_minimum()) {
slider->SetMinimum(base_input->minimum().toLongLong());
if (input_->has_minimum()) {
slider->SetMinimum(input_->minimum().toLongLong());
}
if (base_input->has_maximum()) {
slider->SetMaximum(base_input->maximum().toLongLong());
if (input_->has_maximum()) {
slider->SetMaximum(input_->maximum().toLongLong());
}
connect(slider, SIGNAL(ValueChanged(int64_t)), this, SLOT(WidgetCallback()));
@@ -80,14 +144,12 @@ void NodeParamViewWidgetBridge::CreateWidgets()
{
FloatSlider* slider = new FloatSlider();
slider->SetValue(base_input->get_value_at_time(0).toDouble());
if (base_input->has_minimum()) {
slider->SetMinimum(base_input->minimum().toDouble());
if (input_->has_minimum()) {
slider->SetMinimum(input_->minimum().toDouble());
}
if (base_input->has_maximum()) {
slider->SetMaximum(base_input->maximum().toDouble());
if (input_->has_maximum()) {
slider->SetMaximum(input_->maximum().toDouble());
}
connect(slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
@@ -97,60 +159,45 @@ void NodeParamViewWidgetBridge::CreateWidgets()
}
case NodeParam::kVec2:
{
QVector2D vec2 = base_input->get_value_at_time(0).value<QVector2D>();
FloatSlider* x_slider = new FloatSlider();
x_slider->SetValue(static_cast<double>(vec2.x()));
widgets_.append(x_slider);
connect(x_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
FloatSlider* y_slider = new FloatSlider();
y_slider->SetValue(static_cast<double>(vec2.y()));
widgets_.append(y_slider);
connect(y_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
break;
}
case NodeParam::kVec3:
{
QVector3D vec3 = base_input->get_value_at_time(0).value<QVector3D>();
FloatSlider* x_slider = new FloatSlider();
x_slider->SetValue(static_cast<double>(vec3.x()));
widgets_.append(x_slider);
connect(x_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
FloatSlider* y_slider = new FloatSlider();
y_slider->SetValue(static_cast<double>(vec3.y()));
widgets_.append(y_slider);
connect(y_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
FloatSlider* z_slider = new FloatSlider();
z_slider->SetValue(static_cast<double>(vec3.z()));
widgets_.append(z_slider);
connect(z_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
break;
}
case NodeParam::kVec4:
{
QVector4D vec4 = base_input->get_value_at_time(0).value<QVector4D>();
FloatSlider* x_slider = new FloatSlider();
x_slider->SetValue(static_cast<double>(vec4.x()));
widgets_.append(x_slider);
connect(x_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
FloatSlider* y_slider = new FloatSlider();
y_slider->SetValue(static_cast<double>(vec4.y()));
widgets_.append(y_slider);
connect(y_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
FloatSlider* z_slider = new FloatSlider();
z_slider->SetValue(static_cast<double>(vec4.z()));
widgets_.append(z_slider);
connect(z_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
FloatSlider* w_slider = new FloatSlider();
w_slider->SetValue(static_cast<double>(vec4.w()));
widgets_.append(w_slider);
connect(w_slider, SIGNAL(ValueChanged(double)), this, SLOT(WidgetCallback()));
break;
@@ -171,7 +218,6 @@ void NodeParamViewWidgetBridge::CreateWidgets()
case NodeParam::kBoolean:
{
QCheckBox* check_box = new QCheckBox();
check_box->setChecked(base_input->get_value_at_time(0).toBool());
widgets_.append(check_box);
connect(check_box, SIGNAL(toggled(bool)), this, SLOT(WidgetCallback()));
break;
@@ -185,17 +231,9 @@ void NodeParamViewWidgetBridge::CreateWidgets()
case NodeParam::kFootage:
{
FootageComboBox* footage_combobox = new FootageComboBox();
// FIXME: Test code
// Pretty hacky way of getting the root folder for this node's sequence
ProjectPanel* pp = olive::panel_manager->MostRecentlyFocused<ProjectPanel>();
footage_combobox->SetRoot(pp->project()->root());
// Use multiple values
footage_combobox->SetFootage(base_input->get_value_at_time(0).value<StreamPtr>());
footage_combobox->SetRoot(static_cast<Sequence*>(input_->parentNode()->parent())->project()->root());
connect(footage_combobox, SIGNAL(FootageChanged(StreamPtr)), this, SLOT(WidgetCallback()));
// End test code
widgets_.append(footage_combobox);
@@ -204,135 +242,156 @@ void NodeParamViewWidgetBridge::CreateWidgets()
}
}
void NodeParamViewWidgetBridge::SetInputValue(NodeInput *input, const QVariant &value)
{
QUndoCommand* command = new QUndoCommand();
if (input->is_keyframing()) {
NodeKeyframePtr existing_key = input->get_keyframe_at_time(time_);
if (existing_key) {
new NodeParamSetKeyframeValueCommand(existing_key, value, command);
} else {
// No existing key, create a new one
NodeKeyframePtr closest_key = input->get_closest_keyframe_to_time(time_);
NodeKeyframePtr new_key = std::make_shared<NodeKeyframe>(time_, value, closest_key->type());
new NodeParamInsertKeyframeCommand(input, new_key, command);
}
} else {
new NodeParamSetKeyframeValueCommand(input->keyframes().first(), value, command);
}
olive::undo_stack.pushIfHasChildren(command);
}
void NodeParamViewWidgetBridge::WidgetCallback()
{
foreach (NodeInput* input, inputs_) {
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::kWholeNumber:
case NodeParam::kNumber:
case NodeParam::kString:
case NodeParam::kVector:
case NodeParam::kBuffer:
break;
case NodeParam::kInt:
{
// Widget is a IntegerSlider
IntegerSlider* int_slider = static_cast<IntegerSlider*>(sender());
input->set_value_at_time(0, int_slider->GetValue());
break;
}
case NodeParam::kFloat:
{
// Widget is a FloatSlider
FloatSlider* float_slider = static_cast<FloatSlider*>(sender());
input->set_value_at_time(0, float_slider->GetValue());
break;
}
case NodeParam::kVec2:
{
// Widgets are two FloatSliders
FloatSlider* slider = static_cast<FloatSlider*>(sender());
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::kWholeNumber:
case NodeParam::kNumber:
case NodeParam::kString:
case NodeParam::kVector:
case NodeParam::kBuffer:
break;
case NodeParam::kInt:
{
// Widget is a IntegerSlider
// IntegerSlider* int_slider = static_cast<IntegerSlider*>(sender());
// input->set_default_value(time_, int_slider->GetValue());
break;
}
case NodeParam::kFloat:
{
// Widget is a FloatSlider
FloatSlider* float_slider = static_cast<FloatSlider*>(sender());
SetInputValue(input_, float_slider->GetValue());
break;
}
case NodeParam::kVec2:
{
// Widgets are two FloatSliders
FloatSlider* slider = static_cast<FloatSlider*>(sender());
QVector2D val = input->get_value_at_time(0).value<QVector2D>();
QVector2D val = input_->get_value_at_time(0).value<QVector2D>();
if (slider == widgets_.at(0)) {
// Slider is X slider
val.setX(static_cast<float>(slider->GetValue()));
} else {
// Slider is Y slider
val.setY(static_cast<float>(slider->GetValue()));
}
if (slider == widgets_.at(0)) {
// Slider is X slider
val.setX(static_cast<float>(slider->GetValue()));
} else {
// Slider is Y slider
val.setY(static_cast<float>(slider->GetValue()));
}
input->set_value_at_time(0, val);
break;
}
case NodeParam::kVec3:
{
// Widgets are three FloatSliders
FloatSlider* slider = static_cast<FloatSlider*>(sender());
// input->set_default_value(0, val);
break;
}
case NodeParam::kVec3:
{
// Widgets are three FloatSliders
FloatSlider* slider = static_cast<FloatSlider*>(sender());
QVector3D val = input->get_value_at_time(0).value<QVector3D>();
QVector3D val = input_->get_value_at_time(0).value<QVector3D>();
if (slider == widgets_.at(0)) {
// Slider is X slider
val.setX(static_cast<float>(slider->GetValue()));
} else if (slider == widgets_.at(1)) {
// Slider is Y slider
val.setY(static_cast<float>(slider->GetValue()));
} else {
// Slider is Z slider
val.setZ(static_cast<float>(slider->GetValue()));
}
if (slider == widgets_.at(0)) {
// Slider is X slider
val.setX(static_cast<float>(slider->GetValue()));
} else if (slider == widgets_.at(1)) {
// Slider is Y slider
val.setY(static_cast<float>(slider->GetValue()));
} else {
// Slider is Z slider
val.setZ(static_cast<float>(slider->GetValue()));
}
input->set_value_at_time(0, val);
break;
}
case NodeParam::kVec4:
{
// Widgets are three FloatSliders
FloatSlider* slider = static_cast<FloatSlider*>(sender());
// input->set_default_value(0, val);
break;
}
case NodeParam::kVec4:
{
// Widgets are three FloatSliders
FloatSlider* slider = static_cast<FloatSlider*>(sender());
QVector4D val = input->get_value_at_time(0).value<QVector4D>();
QVector4D val = input_->get_value_at_time(0).value<QVector4D>();
if (slider == widgets_.at(0)) {
// Slider is X slider
val.setX(static_cast<float>(slider->GetValue()));
} else if (slider == widgets_.at(1)) {
// Slider is Y slider
val.setY(static_cast<float>(slider->GetValue()));
} else if (slider == widgets_.at(2)) {
// Slider is Z slider
val.setZ(static_cast<float>(slider->GetValue()));
} else {
// Slider is W slider
val.setW(static_cast<float>(slider->GetValue()));
}
if (slider == widgets_.at(0)) {
// Slider is X slider
val.setX(static_cast<float>(slider->GetValue()));
} else if (slider == widgets_.at(1)) {
// Slider is Y slider
val.setY(static_cast<float>(slider->GetValue()));
} else if (slider == widgets_.at(2)) {
// Slider is Z slider
val.setZ(static_cast<float>(slider->GetValue()));
} else {
// Slider is W slider
val.setW(static_cast<float>(slider->GetValue()));
}
input->set_value_at_time(0, val);
break;
}
case NodeParam::kFile:
// FIXME: File selector
break;
case NodeParam::kColor:
// FIXME: Color selector
break;
case NodeParam::kText:
{
// Sender is a QLineEdit
QLineEdit* line_edit = static_cast<QLineEdit*>(sender());
input->set_value_at_time(0, line_edit->text());
break;
}
case NodeParam::kBoolean:
{
// Widget is a QCheckBox
QCheckBox* check_box = static_cast<QCheckBox*>(sender());
input->set_value_at_time(0, check_box->isChecked());
break;
}
case NodeParam::kFont:
{
// Widget is a QFontComboBox
QFontComboBox* font_combobox = static_cast<QFontComboBox*>(sender());
input->set_value_at_time(0, font_combobox->currentFont());
break;
}
case NodeParam::kFootage:
{
// Widget is a FootageComboBox
FootageComboBox* footage_combobox = static_cast<FootageComboBox*>(sender());
input->set_value_at_time(0, QVariant::fromValue(footage_combobox->SelectedFootage()));
break;
}
}
// input->set_default_value(0, val);
break;
}
case NodeParam::kFile:
// FIXME: File selector
break;
case NodeParam::kColor:
// FIXME: Color selector
break;
case NodeParam::kText:
{
// Sender is a QLineEdit
// QLineEdit* line_edit = static_cast<QLineEdit*>(sender());
// input->set_default_value(time_, line_edit->text());
break;
}
case NodeParam::kBoolean:
{
// Widget is a QCheckBox
// QCheckBox* check_box = static_cast<QCheckBox*>(sender());
// input->set_default_value(0, check_box->isChecked());
break;
}
case NodeParam::kFont:
{
// Widget is a QFontComboBox
// QFontComboBox* font_combobox = static_cast<QFontComboBox*>(sender());
// input->set_default_value(time_, font_combobox->currentFont());
break;
}
case NodeParam::kFootage:
{
// Widget is a FootageComboBox
// FootageComboBox* footage_combobox = static_cast<FootageComboBox*>(sender());
// input->set_default_value(time_, QVariant::fromValue(footage_combobox->SelectedFootage()));
break;
}
}
}
@@ -9,18 +9,22 @@ class NodeParamViewWidgetBridge : public QObject
{
Q_OBJECT
public:
NodeParamViewWidgetBridge(QObject* parent);
NodeParamViewWidgetBridge(NodeInput* input, QObject* parent);
void AddInput(NodeInput* input);
void SetTime(const rational& time);
const QList<QWidget*>& widgets();
private:
QList<NodeInput*> inputs_;
void CreateWidgets();
void SetInputValue(NodeInput* input, const QVariant& value);
NodeInput* input_;
QList<QWidget*> widgets_;
void CreateWidgets();
rational time_;
private slots:
void WidgetCallback();
@@ -32,6 +32,8 @@ void TimelineViewBase::SetScale(const double &scale)
viewport()->update();
end_item_->SetScale(scale_);
ScaleChangedEvent(scale_);
}
void TimelineViewBase::SetTimebase(const rational &timebase)
@@ -146,3 +148,8 @@ void TimelineViewBase::resizeEvent(QResizeEvent *event)
UpdateSceneRect();
}
void TimelineViewBase::ScaleChangedEvent(double scale)
{
Q_UNUSED(scale)
}
@@ -32,6 +32,8 @@ protected:
virtual void resizeEvent(QResizeEvent *event) override;
virtual void ScaleChangedEvent(double scale);
rational GetPlayheadTime();
bool PlayheadPress(QMouseEvent* event);