began new infrastructure
Yep, this is another one of my "famous" sweeping rewrites. Expect things to break. Goals for this are: - Greatly simplify node connections (particularly with arrays) so the code requires less maintenance/is more stable - Redesign node structure to address issues where UI would stall for lengthy periods of time - Less reliance on shared ptrs/greater reliance on QObject system for inheritance/memory management - General code cleanup and improvements
This commit is contained in:
@@ -29,9 +29,9 @@ add_subdirectory(keyframeview)
|
||||
add_subdirectory(manageddisplay)
|
||||
add_subdirectory(menu)
|
||||
add_subdirectory(nodecombobox)
|
||||
add_subdirectory(nodeparamview)
|
||||
add_subdirectory(nodetableview)
|
||||
add_subdirectory(nodetreeview)
|
||||
add_subdirectory(nodeparamview)
|
||||
add_subdirectory(nodeview)
|
||||
add_subdirectory(panel)
|
||||
add_subdirectory(path)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
BezierControlPointItem::BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) :
|
||||
BezierControlPointItem::BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
key_(key),
|
||||
mode_(mode),
|
||||
@@ -38,12 +38,12 @@ BezierControlPointItem::BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe
|
||||
{
|
||||
setFlag(QGraphicsItem::ItemIsMovable);
|
||||
|
||||
connect(key.get(), &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
connect(key, &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
|
||||
if (mode_ == NodeKeyframe::kInHandle) {
|
||||
connect(key.get(), &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
connect(key, &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
} else {
|
||||
connect(key.get(), &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
connect(key, &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ void BezierControlPointItem::SetYScale(double scale)
|
||||
UpdatePos();
|
||||
}
|
||||
|
||||
NodeKeyframePtr BezierControlPointItem::key() const
|
||||
NodeKeyframe* BezierControlPointItem::key() const
|
||||
{
|
||||
return key_;
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ namespace olive {
|
||||
class BezierControlPointItem : public QObject, public QGraphicsRectItem
|
||||
{
|
||||
public:
|
||||
BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr);
|
||||
BezierControlPointItem(NodeKeyframe* key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr);
|
||||
|
||||
void SetXScale(double scale);
|
||||
|
||||
void SetYScale(double scale);
|
||||
|
||||
NodeKeyframePtr key() const;
|
||||
NodeKeyframe* key() const;
|
||||
|
||||
const NodeKeyframe::BezierType& mode() const;
|
||||
|
||||
@@ -50,7 +50,7 @@ protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
NodeKeyframe* key_;
|
||||
|
||||
NodeKeyframe::BezierType mode_;
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
|
||||
#include "curveview.h"
|
||||
|
||||
#include <QScrollBar>
|
||||
#include <QHash>
|
||||
#include <QMouseEvent>
|
||||
#include <QScrollBar>
|
||||
#include <QtMath>
|
||||
#include <cfloat>
|
||||
|
||||
@@ -67,16 +68,9 @@ void CurveView::ConnectInput(NodeInput *input)
|
||||
return;
|
||||
}
|
||||
|
||||
// Add keyframes from this input
|
||||
foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) {
|
||||
foreach (NodeKeyframePtr key, track) {
|
||||
this->AddKeyframe(key);
|
||||
}
|
||||
|
||||
if (!keyframe_colors_.contains(&track)) {
|
||||
// Generate a random color for this input
|
||||
keyframe_colors_.insert(&track, QColor::fromHsv(std::rand()%360, std::rand()%255, 255));
|
||||
}
|
||||
// Add keyframes from each subelement including primary (-1)
|
||||
for (int i=-1; i<input->ArraySize(); i++) {
|
||||
ConnectInputElement(input, i);
|
||||
}
|
||||
|
||||
// Append to the list
|
||||
@@ -89,9 +83,7 @@ void CurveView::ConnectInput(NodeInput *input)
|
||||
|
||||
void CurveView::DisconnectNode(Node *node)
|
||||
{
|
||||
QVector<NodeInput*> inputs = node->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
foreach (NodeInput* i, node->parameters()) {
|
||||
DisconnectInput(i);
|
||||
}
|
||||
}
|
||||
@@ -166,82 +158,89 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
|
||||
// Draw keyframe lines
|
||||
foreach (NodeInput* input, connected_inputs_) {
|
||||
if (input->is_keyframing()) {
|
||||
foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) {
|
||||
if (!track.isEmpty()) {
|
||||
painter->setPen(QPen(keyframe_colors_.value(&track), qMax(1, fontMetrics().height() / 4)));
|
||||
for (int j=-1; j<input->ArraySize(); j++) {
|
||||
if (input->IsKeyframing(j)) {
|
||||
const QVector<NodeKeyframeTrack>& tracks = input->GetKeyframeTracks(j);
|
||||
|
||||
QVector<QLineF> keyframe_lines;
|
||||
for (int k=0; k<tracks.size(); k++) {
|
||||
const NodeKeyframeTrack& track = tracks.at(k);
|
||||
|
||||
// Draw straight line leading to first keyframe
|
||||
QPointF first_key_pos = item_map().value(track.first().get())->pos();
|
||||
keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos));
|
||||
if (!track.isEmpty()) {
|
||||
painter->setPen(QPen(keyframe_colors_.value(GetKeyframeTrackUniqueID(input, j, k)),
|
||||
qMax(1, fontMetrics().height() / 4)));
|
||||
|
||||
// Draw lines between each keyframe
|
||||
for (int i=1;i<track.size();i++) {
|
||||
NodeKeyframePtr before = track.at(i-1);
|
||||
NodeKeyframePtr after = track.at(i);
|
||||
QVector<QLineF> keyframe_lines;
|
||||
|
||||
KeyframeViewItem* before_item = item_map().value(before.get());
|
||||
KeyframeViewItem* after_item = item_map().value(after.get());
|
||||
// Draw straight line leading to first keyframe
|
||||
QPointF first_key_pos = item_map().value(track.first())->pos();
|
||||
keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos));
|
||||
|
||||
if (before->type() == NodeKeyframe::kHold) {
|
||||
// Draw a hold keyframe (basically a right angle)
|
||||
keyframe_lines.append(QLineF(before_item->pos().x(),
|
||||
before_item->pos().y(),
|
||||
after_item->pos().x(),
|
||||
before_item->pos().y()));
|
||||
keyframe_lines.append(QLineF(after_item->pos().x(),
|
||||
before_item->pos().y(),
|
||||
after_item->pos().x(),
|
||||
after_item->pos().y()));
|
||||
} else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) {
|
||||
// Draw a cubic bezier
|
||||
// Draw lines between each keyframe
|
||||
for (int i=1;i<track.size();i++) {
|
||||
NodeKeyframe* before = track.at(i-1);
|
||||
NodeKeyframe* after = track.at(i);
|
||||
|
||||
// Cubic beziers have two control points, so we can just use both
|
||||
QPointF before_control_point = before_item->pos() + ScalePoint(before->bezier_control_out());
|
||||
QPointF after_control_point = after_item->pos() + ScalePoint(after->bezier_control_in());
|
||||
KeyframeViewItem* before_item = item_map().value(before);
|
||||
KeyframeViewItem* after_item = item_map().value(after);
|
||||
|
||||
QPainterPath path;
|
||||
path.moveTo(before_item->pos());
|
||||
path.cubicTo(before_control_point, after_control_point, after_item->pos());
|
||||
painter->drawPath(path);
|
||||
if (before->type() == NodeKeyframe::kHold) {
|
||||
// Draw a hold keyframe (basically a right angle)
|
||||
keyframe_lines.append(QLineF(before_item->pos().x(),
|
||||
before_item->pos().y(),
|
||||
after_item->pos().x(),
|
||||
before_item->pos().y()));
|
||||
keyframe_lines.append(QLineF(after_item->pos().x(),
|
||||
before_item->pos().y(),
|
||||
after_item->pos().x(),
|
||||
after_item->pos().y()));
|
||||
} else if (before->type() == NodeKeyframe::kBezier && after->type() == NodeKeyframe::kBezier) {
|
||||
// Draw a cubic bezier
|
||||
|
||||
} else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) {
|
||||
// Draw a quadratic bezier
|
||||
// Cubic beziers have two control points, so we can just use both
|
||||
QPointF before_control_point = before_item->pos() + ScalePoint(before->bezier_control_out());
|
||||
QPointF after_control_point = after_item->pos() + ScalePoint(after->bezier_control_in());
|
||||
|
||||
// Quadratic beziers have a single control point, we just have to determine which it is
|
||||
QPointF key_anchor;
|
||||
QPointF control_point;
|
||||
QPainterPath path;
|
||||
path.moveTo(before_item->pos());
|
||||
path.cubicTo(before_control_point, after_control_point, after_item->pos());
|
||||
painter->drawPath(path);
|
||||
|
||||
} else if (before->type() == NodeKeyframe::kBezier || after->type() == NodeKeyframe::kBezier) {
|
||||
// Draw a quadratic bezier
|
||||
|
||||
// Quadratic beziers have a single control point, we just have to determine which it is
|
||||
QPointF key_anchor;
|
||||
QPointF control_point;
|
||||
|
||||
if (before->type() == NodeKeyframe::kBezier) {
|
||||
key_anchor = before_item->pos();
|
||||
control_point = before->bezier_control_out();
|
||||
} else {
|
||||
key_anchor = after_item->pos();
|
||||
control_point = after->bezier_control_in();
|
||||
}
|
||||
|
||||
// Scale control point
|
||||
control_point = key_anchor + ScalePoint(control_point);
|
||||
|
||||
// Create the path from both keyframes
|
||||
QPainterPath path;
|
||||
path.moveTo(before_item->pos());
|
||||
path.quadTo(control_point, after_item->pos());
|
||||
painter->drawPath(path);
|
||||
|
||||
if (before->type() == NodeKeyframe::kBezier) {
|
||||
key_anchor = before_item->pos();
|
||||
control_point = before->bezier_control_out();
|
||||
} else {
|
||||
key_anchor = after_item->pos();
|
||||
control_point = after->bezier_control_in();
|
||||
// Linear to linear
|
||||
keyframe_lines.append(QLineF(before_item->pos(), after_item->pos()));
|
||||
}
|
||||
|
||||
// Scale control point
|
||||
control_point = key_anchor + ScalePoint(control_point);
|
||||
|
||||
// Create the path from both keyframes
|
||||
QPainterPath path;
|
||||
path.moveTo(before_item->pos());
|
||||
path.quadTo(control_point, after_item->pos());
|
||||
painter->drawPath(path);
|
||||
|
||||
} else {
|
||||
// Linear to linear
|
||||
keyframe_lines.append(QLineF(before_item->pos(), after_item->pos()));
|
||||
}
|
||||
|
||||
// Draw straight line leading from end keyframe
|
||||
QPointF last_key_pos = item_map().value(track.last())->pos();
|
||||
keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y())));
|
||||
|
||||
painter->drawLines(keyframe_lines);
|
||||
}
|
||||
|
||||
// Draw straight line leading from end keyframe
|
||||
QPointF last_key_pos = item_map().value(track.last().get())->pos();
|
||||
keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y())));
|
||||
|
||||
painter->drawLines(keyframe_lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,7 +283,7 @@ void CurveView::VerticalScaleChangedEvent(double scale)
|
||||
QMap<NodeKeyframe*, KeyframeViewItem*>::const_iterator iterator;
|
||||
|
||||
for (iterator=item_map().begin();iterator!=item_map().end();iterator++) {
|
||||
SetItemYFromKeyframeValue(iterator.value()->key().get(), iterator.value());
|
||||
SetItemYFromKeyframeValue(iterator.value()->key(), iterator.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +305,26 @@ void CurveView::ContextMenuEvent(Menu &m)
|
||||
//QAction* reset_zoom_action = m.addAction(tr("Reset Zoom"));
|
||||
}
|
||||
|
||||
void CurveView::ConnectInputElement(NodeInput *input, int element)
|
||||
{
|
||||
const QVector<NodeKeyframeTrack>& tracks = input->GetKeyframeTracks(element);
|
||||
|
||||
for (int i=0; i<tracks.size(); i++) {
|
||||
const NodeKeyframeTrack& track = tracks.at(i);
|
||||
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
this->AddKeyframe(key);
|
||||
}
|
||||
|
||||
uint h = GetKeyframeTrackUniqueID(input, element, i);
|
||||
|
||||
if (!keyframe_colors_.contains(h)) {
|
||||
// Generate a random color for this input
|
||||
keyframe_colors_.insert(h, QColor::fromHsv(std::rand()%360, std::rand()%255, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qreal CurveView::GetItemYFromKeyframeValue(NodeKeyframe *key)
|
||||
{
|
||||
return GetItemYFromKeyframeValue(key->value().toDouble());
|
||||
@@ -340,6 +359,11 @@ void CurveView::CreateBezierControlPoints(KeyframeViewItem* item)
|
||||
connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
uint CurveView::GetKeyframeTrackUniqueID(NodeInput *input, int element, int track)
|
||||
{
|
||||
return ::qHash(input) ^ ::qHash(element) ^ ::qHash(track);
|
||||
}
|
||||
|
||||
void CurveView::KeyframeValueChanged()
|
||||
{
|
||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
@@ -398,10 +422,10 @@ void CurveView::ZoomToFit()
|
||||
double max_val = DBL_MIN;
|
||||
|
||||
for (auto i=item_map().constBegin(); i!=item_map().constEnd(); i++) {
|
||||
rational transformed_time = GetAdjustedTime(i.key()->parent()->parentNode(),
|
||||
rational transformed_time = GetAdjustedTime(i.key()->parent()->parent(),
|
||||
GetTimeTarget(),
|
||||
i.key()->time(),
|
||||
NodeParam::kOutput);
|
||||
false);
|
||||
|
||||
min_time = qMin(transformed_time, min_time);
|
||||
max_time = qMax(transformed_time, max_time);
|
||||
@@ -421,14 +445,14 @@ void CurveView::ZoomToFit()
|
||||
verticalScrollBar()->setValue(GetItemYFromKeyframeValue(max_val) - CalculatePaddingFromDimensionScale(this->height()));
|
||||
}
|
||||
|
||||
void CurveView::AddKeyframe(NodeKeyframePtr key)
|
||||
void CurveView::AddKeyframe(NodeKeyframe* key)
|
||||
{
|
||||
KeyframeViewItem* item = AddKeyframeInternal(key);
|
||||
SetItemYFromKeyframeValue(key.get(), item);
|
||||
item->SetOverrideBrush(keyframe_colors_.value(&key->parent()->keyframe_tracks().at(key->track())));
|
||||
SetItemYFromKeyframeValue(key, item);
|
||||
item->SetOverrideBrush(keyframe_colors_.value(GetKeyframeTrackUniqueID(key->parent(), key->element(), key->track())));
|
||||
|
||||
connect(key.get(), &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
|
||||
connect(key.get(), &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged);
|
||||
connect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
|
||||
connect(key, &NodeKeyframe::TypeChanged, this, &CurveView::KeyframeTypeChanged);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
void DisconnectInput(NodeInput* input);
|
||||
|
||||
public slots:
|
||||
void AddKeyframe(NodeKeyframePtr key);
|
||||
void AddKeyframe(NodeKeyframe* key);
|
||||
|
||||
void ZoomToFit();
|
||||
|
||||
@@ -63,6 +63,8 @@ protected:
|
||||
virtual void ContextMenuEvent(Menu &m) override;
|
||||
|
||||
private:
|
||||
void ConnectInputElement(NodeInput* input, int element);
|
||||
|
||||
qreal GetItemYFromKeyframeValue(NodeKeyframe* key);
|
||||
qreal GetItemYFromKeyframeValue(double value);
|
||||
|
||||
@@ -74,7 +76,9 @@ private:
|
||||
|
||||
void CreateBezierControlPoints(KeyframeViewItem *item);
|
||||
|
||||
QMap<const NodeInput::KeyframeTrack*, QColor> keyframe_colors_;
|
||||
uint GetKeyframeTrackUniqueID(NodeInput* input, int element, int track);
|
||||
|
||||
QMap<uint, QColor> keyframe_colors_;
|
||||
|
||||
int text_padding_;
|
||||
|
||||
|
||||
@@ -215,9 +215,7 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp)
|
||||
|
||||
void CurveWidget::ConnectNode(Node *n)
|
||||
{
|
||||
QVector<NodeInput*> inputs = n->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
foreach (NodeInput* i, n->parameters()) {
|
||||
if (tree_view_->IsInputEnabled(i)) {
|
||||
view_->ConnectInput(i);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ void KeyframeView::SceneRectUpdateEvent(QRectF &rect)
|
||||
rect.setHeight(max_scroll_);
|
||||
}
|
||||
|
||||
void KeyframeView::AddKeyframe(NodeKeyframePtr key, int y)
|
||||
void KeyframeView::AddKeyframe(NodeKeyframe* key, int y)
|
||||
{
|
||||
QPoint global_pt(0, y);
|
||||
QPoint local_pt = mapFromGlobal(global_pt);
|
||||
|
||||
@@ -42,7 +42,7 @@ protected:
|
||||
virtual void SceneRectUpdateEvent(QRectF& rect) override;
|
||||
|
||||
public slots:
|
||||
void AddKeyframe(NodeKeyframePtr key, int y);
|
||||
void AddKeyframe(NodeKeyframe* key, int y);
|
||||
|
||||
private:
|
||||
int max_scroll_;
|
||||
|
||||
@@ -63,11 +63,7 @@ void KeyframeViewBase::DeleteSelected()
|
||||
|
||||
for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) {
|
||||
if (i.value()->isSelected()) {
|
||||
NodeInput* input_parent = i.key()->parent();
|
||||
|
||||
new NodeParamRemoveKeyframeCommand(input_parent,
|
||||
input_parent->get_keyframe_shared_ptr_from_raw(i.key()),
|
||||
command);
|
||||
new NodeParamRemoveKeyframeCommand(i.key(), command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,38 +72,38 @@ void KeyframeViewBase::DeleteSelected()
|
||||
|
||||
void KeyframeViewBase::RemoveKeyframesOfNode(Node *n)
|
||||
{
|
||||
QVector<NodeInput*> inputs = n->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
foreach (NodeInput* i, n->inputs()) {
|
||||
RemoveKeyframesOfInput(i);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *i)
|
||||
void KeyframeViewBase::RemoveKeyframesOfInput(NodeInput *input)
|
||||
{
|
||||
foreach (const NodeInput::KeyframeTrack& track, i->keyframe_tracks()) {
|
||||
foreach (NodeKeyframePtr key, track) {
|
||||
RemoveKeyframe(key);
|
||||
for (int i=-1; i<input->ArraySize(); i++) {
|
||||
foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(i)) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
RemoveKeyframe(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyframeViewBase::RemoveKeyframe(NodeKeyframePtr key)
|
||||
void KeyframeViewBase::RemoveKeyframe(NodeKeyframe* key)
|
||||
{
|
||||
KeyframeAboutToBeRemoved(key.get());
|
||||
KeyframeAboutToBeRemoved(key);
|
||||
|
||||
delete item_map_.take(key.get());
|
||||
delete item_map_.take(key);
|
||||
}
|
||||
|
||||
KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframePtr key)
|
||||
KeyframeViewItem *KeyframeViewBase::AddKeyframeInternal(NodeKeyframe* key)
|
||||
{
|
||||
KeyframeViewItem* item = item_map_.value(key.get());
|
||||
KeyframeViewItem* item = item_map_.value(key);
|
||||
|
||||
if (!item) {
|
||||
item = new KeyframeViewItem(key);
|
||||
item->SetTimeTarget(GetTimeTarget());
|
||||
item->SetScale(GetScale());
|
||||
item_map_.insert(key.get(), item);
|
||||
item_map_.insert(key, item);
|
||||
scene()->addItem(item);
|
||||
}
|
||||
|
||||
@@ -151,7 +147,7 @@ void KeyframeViewBase::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
selected_keys_.replace(i, {key,
|
||||
key->x(),
|
||||
GetAdjustedTime(key->key()->parent()->parentNode(), GetTimeTarget(), key->key()->time(), NodeParam::kOutput),
|
||||
GetAdjustedTime(key->key()->parent()->parent(), GetTimeTarget(), key->key()->time(), false),
|
||||
key->key()->value().toDouble()});
|
||||
}
|
||||
}
|
||||
@@ -184,9 +180,9 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
|
||||
//input_parent->blockSignals(true);
|
||||
|
||||
rational node_time = GetAdjustedTime(GetTimeTarget(),
|
||||
keypair.key->key()->parent()->parentNode(),
|
||||
keypair.key->key()->parent()->parent(),
|
||||
CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()),
|
||||
NodeParam::kInput);
|
||||
true);
|
||||
|
||||
keypair.key->key()->set_time(node_time);
|
||||
|
||||
@@ -235,9 +231,9 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event)
|
||||
|
||||
// Calculate the new time for this keyframe
|
||||
rational node_time = GetAdjustedTime(GetTimeTarget(),
|
||||
keypair.key->key()->parent()->parentNode(),
|
||||
keypair.key->key()->parent()->parent(),
|
||||
CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()),
|
||||
NodeParam::kInput);
|
||||
true);
|
||||
|
||||
|
||||
|
||||
@@ -493,7 +489,7 @@ void KeyframeViewBase::ShowContextMenu()
|
||||
void KeyframeViewBase::ShowKeyframePropertiesDialog()
|
||||
{
|
||||
QList<QGraphicsItem*> items = scene()->selectedItems();
|
||||
QList<NodeKeyframePtr> keys;
|
||||
QVector<NodeKeyframe*> keys;
|
||||
|
||||
foreach (QGraphicsItem* item, items) {
|
||||
keys.append(static_cast<KeyframeViewItem*>(item)->key());
|
||||
@@ -521,15 +517,15 @@ void KeyframeViewBase::AutoSelectKeyTimeNeighbors()
|
||||
|
||||
rational key_time = key_item->key()->time();
|
||||
|
||||
QList<NodeKeyframePtr> keys = key_item->key()->parent()->get_keyframe_at_time(key_time);
|
||||
QVector<NodeKeyframe*> keys = key_item->key()->parent()->GetKeyframesAtTime(key_time, key_item->key()->element());
|
||||
|
||||
foreach (NodeKeyframePtr k, keys) {
|
||||
foreach (NodeKeyframe* k, keys) {
|
||||
if (k == key_item->key()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure this key is not already selected
|
||||
KeyframeViewItem* item = item_map_.value(k.get());
|
||||
KeyframeViewItem* item = item_map_.value(k);
|
||||
|
||||
item->setSelected(true);
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ public:
|
||||
|
||||
void RemoveKeyframesOfNode(Node* n);
|
||||
|
||||
void RemoveKeyframesOfInput(NodeInput* i);
|
||||
void RemoveKeyframesOfInput(NodeInput* input);
|
||||
|
||||
public slots:
|
||||
void RemoveKeyframe(NodeKeyframePtr key);
|
||||
void RemoveKeyframe(NodeKeyframe* key);
|
||||
|
||||
protected:
|
||||
virtual KeyframeViewItem* AddKeyframeInternal(NodeKeyframePtr key);
|
||||
virtual KeyframeViewItem* AddKeyframeInternal(NodeKeyframe* key);
|
||||
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) :
|
||||
KeyframeViewItem::KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
key_(key),
|
||||
scale_(1.0),
|
||||
@@ -39,8 +39,8 @@ KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) :
|
||||
{
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
|
||||
connect(key.get(), &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos);
|
||||
connect(key.get(), &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw);
|
||||
connect(key, &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos);
|
||||
connect(key, &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw);
|
||||
|
||||
int keyframe_size = QtUtils::QFontMetricsWidth(qApp->fontMetrics(), "Oi");
|
||||
int half_sz = keyframe_size/2;
|
||||
@@ -69,7 +69,7 @@ void KeyframeViewItem::SetOverrideBrush(const QBrush &b)
|
||||
setBrush(b);
|
||||
}
|
||||
|
||||
NodeKeyframePtr KeyframeViewItem::key() const
|
||||
NodeKeyframe* KeyframeViewItem::key() const
|
||||
{
|
||||
return key_;
|
||||
}
|
||||
@@ -117,7 +117,7 @@ void KeyframeViewItem::TimeTargetChangedEvent(Node *)
|
||||
|
||||
void KeyframeViewItem::UpdatePos()
|
||||
{
|
||||
rational adjusted = GetAdjustedTime(key_->parent()->parentNode(), GetTimeTarget(), key_->time(), NodeParam::kOutput);
|
||||
rational adjusted = GetAdjustedTime(key_->parent()->parent(), GetTimeTarget(), key_->time(), false);
|
||||
|
||||
setPos(adjusted.toDouble() * scale_, vert_center_);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class KeyframeViewItem : public QObject, public QGraphicsRectItem, public TimeTa
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent = nullptr);
|
||||
KeyframeViewItem(NodeKeyframe* key, QGraphicsItem *parent = nullptr);
|
||||
|
||||
void SetOverrideY(qreal vertical_center);
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
void SetOverrideBrush(const QBrush& b);
|
||||
|
||||
NodeKeyframePtr key() const;
|
||||
NodeKeyframe* key() const;
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
@@ -48,7 +48,7 @@ protected:
|
||||
virtual void TimeTargetChangedEvent(Node* ) override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
NodeKeyframe* key_;
|
||||
|
||||
double scale_;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe::Type type, QUndoCommand *parent) :
|
||||
KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
key_(key),
|
||||
old_type_(key->type()),
|
||||
@@ -36,7 +36,7 @@ KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe
|
||||
|
||||
Project *KeyframeSetTypeCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(key_->parent()->parentNode()->parent())->project();
|
||||
return key_->parent()->parent()->parent()->project();
|
||||
}
|
||||
|
||||
void KeyframeSetTypeCommand::redo_internal()
|
||||
@@ -49,7 +49,7 @@ void KeyframeSetTypeCommand::undo_internal()
|
||||
key_->set_type(old_type_);
|
||||
}
|
||||
|
||||
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand *parent) :
|
||||
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
key_(key),
|
||||
mode_(mode),
|
||||
@@ -58,7 +58,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key
|
||||
{
|
||||
}
|
||||
|
||||
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF &new_point, const QPointF &old_point, QUndoCommand *parent) :
|
||||
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF &new_point, const QPointF &old_point, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
key_(key),
|
||||
mode_(mode),
|
||||
@@ -69,7 +69,7 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key
|
||||
|
||||
Project *KeyframeSetBezierControlPoint::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(key_->parent()->parentNode()->parent())->project();
|
||||
return key_->parent()->parent()->parent()->project();
|
||||
}
|
||||
|
||||
void KeyframeSetBezierControlPoint::redo_internal()
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace olive {
|
||||
|
||||
class KeyframeSetTypeCommand : public UndoCommand {
|
||||
public:
|
||||
KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe::Type type, QUndoCommand* parent = nullptr);
|
||||
KeyframeSetTypeCommand(NodeKeyframe* key, NodeKeyframe::Type type, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -37,7 +37,7 @@ protected:
|
||||
virtual void undo_internal() override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
NodeKeyframe* key_;
|
||||
|
||||
NodeKeyframe::Type old_type_;
|
||||
|
||||
@@ -47,8 +47,8 @@ private:
|
||||
|
||||
class KeyframeSetBezierControlPoint : public UndoCommand {
|
||||
public:
|
||||
KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand* parent = nullptr);
|
||||
KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& new_point, const QPointF& old_point, QUndoCommand* parent = nullptr);
|
||||
KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand* parent = nullptr);
|
||||
KeyframeSetBezierControlPoint(NodeKeyframe* key, NodeKeyframe::BezierType mode, const QPointF& new_point, const QPointF& old_point, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -57,7 +57,7 @@ protected:
|
||||
virtual void undo_internal() override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
NodeKeyframe* key_;
|
||||
|
||||
NodeKeyframe::BezierType mode_;
|
||||
|
||||
|
||||
@@ -151,7 +151,6 @@ void NodeParamView::SelectNodes(const QVector<Node *> &nodes)
|
||||
connect(item, &NodeParamViewItem::KeyframeAdded, keyframe_view_, &KeyframeView::AddKeyframe);
|
||||
connect(item, &NodeParamViewItem::KeyframeRemoved, keyframe_view_, &KeyframeView::RemoveKeyframe);
|
||||
connect(item, &NodeParamViewItem::RequestSetTime, this, &NodeParamView::ItemRequestedTimeChanged);
|
||||
connect(item, &NodeParamViewItem::InputDoubleClicked, this, &NodeParamView::InputDoubleClicked);
|
||||
connect(item, &NodeParamViewItem::RequestSelectNode, this, &NodeParamView::RequestSelectNode);
|
||||
connect(item, &NodeParamViewItem::dockLocationChanged, this, &NodeParamView::QueueKeyframePositionUpdate);
|
||||
connect(item, &NodeParamViewItem::dockLocationChanged, this, &NodeParamView::SignalNodeOrder);
|
||||
|
||||
@@ -73,8 +73,6 @@ public:
|
||||
void DeleteSelected();
|
||||
|
||||
signals:
|
||||
void InputDoubleClicked(NodeInput* input);
|
||||
|
||||
void RequestSelectNode(const QVector<Node*>& target);
|
||||
|
||||
void NodeOrderChanged(const QVector<Node*>& nodes);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInputArray* array, QWidget* parent) :
|
||||
NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInput *array, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
array_(array)
|
||||
{
|
||||
@@ -40,19 +40,19 @@ NodeParamViewArrayWidget::NodeParamViewArrayWidget(NodeInputArray* array, QWidge
|
||||
layout->addWidget(plus_btn_);
|
||||
|
||||
connect(plus_btn_, &QPushButton::clicked, this, &NodeParamViewArrayWidget::AddElement);
|
||||
connect(array_, &NodeInputArray::SizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter);
|
||||
connect(array_, &NodeInput::ArraySizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter);
|
||||
|
||||
UpdateCounter();
|
||||
}
|
||||
|
||||
void NodeParamViewArrayWidget::UpdateCounter()
|
||||
{
|
||||
count_lbl_->setText(tr("%1 element(s)").arg(array_->GetSize()));
|
||||
count_lbl_->setText(tr("%1 element(s)").arg(array_->ArraySize()));
|
||||
}
|
||||
|
||||
void NodeParamViewArrayWidget::AddElement()
|
||||
{
|
||||
array_->Append();
|
||||
array_->ArrayResize(array_->ArraySize() + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
#include "node/inputarray.h"
|
||||
#include "node/input.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -33,10 +33,10 @@ class NodeParamViewArrayWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeParamViewArrayWidget(NodeInputArray* array, QWidget* parent = nullptr);
|
||||
NodeParamViewArrayWidget(NodeInput* array, QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
NodeInputArray* array_;
|
||||
NodeInput* array_;
|
||||
|
||||
QLabel* count_lbl_;
|
||||
|
||||
|
||||
@@ -30,9 +30,10 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidget *parent) :
|
||||
NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, int element, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
input_(input)
|
||||
input_(input),
|
||||
element_(element)
|
||||
{
|
||||
QHBoxLayout* layout = new QHBoxLayout(this);
|
||||
layout->setSpacing(QtUtils::QFontMetricsWidth(fontMetrics(), QStringLiteral(" ")));
|
||||
@@ -55,18 +56,25 @@ NodeParamViewConnectedLabel::NodeParamViewConnectedLabel(NodeInput *input, QWidg
|
||||
connected_to_lbl_->setForegroundRole(QPalette::Link);
|
||||
connected_to_lbl_->setFont(link_font);
|
||||
|
||||
UpdateConnected();
|
||||
UpdateConnected(nullptr, element_);
|
||||
|
||||
connect(input_, &NodeInput::EdgeAdded, this, &NodeParamViewConnectedLabel::UpdateConnected);
|
||||
connect(input_, &NodeInput::EdgeRemoved, this, &NodeParamViewConnectedLabel::UpdateConnected);
|
||||
connect(input_, &NodeInput::InputConnected, this, &NodeParamViewConnectedLabel::UpdateConnected);
|
||||
connect(input_, &NodeInput::InputDisconnected, this, &NodeParamViewConnectedLabel::UpdateConnected);
|
||||
}
|
||||
|
||||
void NodeParamViewConnectedLabel::UpdateConnected()
|
||||
void NodeParamViewConnectedLabel::UpdateConnected(Node *src, int element)
|
||||
{
|
||||
Q_UNUSED(src)
|
||||
|
||||
if (element_ != element) {
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
QString connection_str;
|
||||
|
||||
if (input_->is_connected()) {
|
||||
connection_str = input_->get_connected_node()->Name();
|
||||
if (input_->IsConnected(element_)) {
|
||||
connection_str = input_->GetConnectedNode(element_)->Name();
|
||||
} else {
|
||||
connection_str = tr("Nothing");
|
||||
}
|
||||
@@ -80,7 +88,7 @@ void NodeParamViewConnectedLabel::ShowLabelContextMenu()
|
||||
|
||||
QAction* disconnect_action = m.addAction(tr("Disconnect"));
|
||||
connect(disconnect_action, &QAction::triggered, this, [this](){
|
||||
Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(input_->get_connected_output(), input_));
|
||||
Core::instance()->undo_stack()->push(new NodeEdgeRemoveCommand(input_->GetConnectedNode(element_), input_, element_));
|
||||
});
|
||||
|
||||
m.exec(QCursor::pos());
|
||||
|
||||
@@ -29,13 +29,13 @@ namespace olive {
|
||||
class NodeParamViewConnectedLabel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeParamViewConnectedLabel(NodeInput* input, QWidget* parent = nullptr);
|
||||
NodeParamViewConnectedLabel(NodeInput* input, int element, QWidget* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void ConnectionClicked();
|
||||
|
||||
private slots:
|
||||
void UpdateConnected();
|
||||
void UpdateConnected(Node* src, int element);
|
||||
|
||||
void ShowLabelContextMenu();
|
||||
|
||||
@@ -44,6 +44,8 @@ private:
|
||||
|
||||
NodeInput* input_;
|
||||
|
||||
int element_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -43,17 +43,14 @@ NodeParamViewItem::NodeParamViewItem(Node *node, QWidget *parent) :
|
||||
this->setTitleBarWidget(title_bar_);
|
||||
|
||||
// Create and add contents widget
|
||||
QVector<NodeInput*> inputs;
|
||||
QVector<Node::InputConnection> inputs;
|
||||
|
||||
// Filter out inputs
|
||||
foreach (NodeParam* p, node->parameters()) {
|
||||
if (p->type() == NodeParam::kInput) {
|
||||
inputs.append(static_cast<NodeInput*>(p));
|
||||
}
|
||||
foreach (NodeInput* p, node->parameters()) {
|
||||
inputs.append({p, -1});
|
||||
}
|
||||
|
||||
body_ = new NodeParamViewItemBody(inputs);
|
||||
connect(body_, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItem::InputDoubleClicked);
|
||||
connect(body_, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItem::RequestSelectNode);
|
||||
connect(body_, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItem::RequestSetTime);
|
||||
connect(body_, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItem::KeyframeAdded);
|
||||
@@ -206,7 +203,7 @@ void NodeParamViewItemTitleBar::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
collapse_btn_->click();
|
||||
}
|
||||
|
||||
NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeInput *> &inputs, QWidget *parent) :
|
||||
NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeConnectable::InputConnection> &inputs, QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
int row_count = 0;
|
||||
@@ -215,14 +212,13 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeInput *> &inputs,
|
||||
|
||||
QGridLayout* content_layout = new QGridLayout(this);
|
||||
|
||||
foreach (NodeInput* input, inputs) {
|
||||
foreach (const NodeConnectable::InputConnection& conn, inputs) {
|
||||
InputUI ui_objects;
|
||||
|
||||
// Add descriptor label
|
||||
ui_objects.main_label = new ClickableLabel();
|
||||
connect(ui_objects.main_label, &ClickableLabel::MouseDoubleClicked, this, &NodeParamViewItemBody::LabelDoubleClicked);
|
||||
|
||||
if (input->IsArray()) {
|
||||
if (conn.input->IsArray()) {
|
||||
QHBoxLayout* array_label_layout = new QHBoxLayout();
|
||||
array_label_layout->setMargin(0);
|
||||
|
||||
@@ -233,7 +229,13 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeInput *> &inputs,
|
||||
|
||||
content_layout->addLayout(array_label_layout, row_count, 0);
|
||||
|
||||
NodeParamViewItemBody* sub_body = new NodeParamViewItemBody(static_cast<NodeInputArray*>(input)->sub_params());
|
||||
QVector<NodeConnectable::InputConnection> subelements(conn.input->ArraySize());
|
||||
|
||||
for (int i=0; i<conn.input->ArraySize(); i++) {
|
||||
subelements[i] = {conn.input, i};
|
||||
}
|
||||
|
||||
NodeParamViewItemBody* sub_body = new NodeParamViewItemBody(subelements);
|
||||
sub_bodies_.append(sub_body);
|
||||
sub_body->layout()->setMargin(0);
|
||||
content_layout->addWidget(sub_body, row_count + 1, 0, 1, max_col + 1);
|
||||
@@ -243,14 +245,13 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeInput *> &inputs,
|
||||
connect(sub_body, &NodeParamViewItemBody::KeyframeAdded, this, &NodeParamViewItemBody::KeyframeAdded);
|
||||
connect(sub_body, &NodeParamViewItemBody::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved);
|
||||
connect(sub_body, &NodeParamViewItemBody::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime);
|
||||
connect(sub_body, &NodeParamViewItemBody::InputDoubleClicked, this, &NodeParamViewItemBody::InputDoubleClicked);
|
||||
connect(sub_body, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItemBody::RequestSelectNode);
|
||||
} else {
|
||||
content_layout->addWidget(ui_objects.main_label, row_count, 0);
|
||||
}
|
||||
|
||||
// Create a widget/input bridge for this input
|
||||
ui_objects.widget_bridge = new NodeParamViewWidgetBridge(input, this);
|
||||
ui_objects.widget_bridge = new NodeParamViewWidgetBridge(conn.input, conn.element, this);
|
||||
|
||||
// Add widgets for this parameter to the layout
|
||||
{
|
||||
@@ -261,42 +262,44 @@ NodeParamViewItemBody::NodeParamViewItemBody(const QVector<NodeInput *> &inputs,
|
||||
}
|
||||
}
|
||||
|
||||
if (input->is_connectable()) {
|
||||
if (conn.input->IsConnectable()) {
|
||||
// Create clickable label used when an input is connected
|
||||
ui_objects.connected_label = new NodeParamViewConnectedLabel(input);
|
||||
ui_objects.connected_label = new NodeParamViewConnectedLabel(conn.input, conn.element);
|
||||
connect(ui_objects.connected_label, &NodeParamViewConnectedLabel::ConnectionClicked, this, &NodeParamViewItemBody::ConnectionClicked);
|
||||
content_layout->addWidget(ui_objects.connected_label, row_count, 1);
|
||||
|
||||
connect(input, &NodeInput::EdgeAdded, this, &NodeParamViewItemBody::EdgeChanged);
|
||||
connect(input, &NodeInput::EdgeRemoved, this, &NodeParamViewItemBody::EdgeChanged);
|
||||
connect(conn.input, &NodeInput::InputConnected, this, &NodeParamViewItemBody::EdgeChanged);
|
||||
connect(conn.input, &NodeInput::InputDisconnected, this, &NodeParamViewItemBody::EdgeChanged);
|
||||
}
|
||||
|
||||
// Add keyframe control to this layout if parameter is keyframable
|
||||
if (input->is_keyframable()) {
|
||||
if (conn.input->IsKeyframable()) {
|
||||
// Hacky but effective way to make sure this widget is always as far right as possible
|
||||
int control_column = max_col;
|
||||
|
||||
ui_objects.key_control = new NodeParamViewKeyframeControl();
|
||||
ui_objects.key_control->SetInput(input);
|
||||
ui_objects.key_control->SetInput(conn.input, conn.element);
|
||||
content_layout->addWidget(ui_objects.key_control, row_count, control_column);
|
||||
connect(ui_objects.key_control, &NodeParamViewKeyframeControl::RequestSetTime, this, &NodeParamViewItemBody::RequestSetTime);
|
||||
|
||||
connect(input, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewItemBody::InputKeyframeEnableChanged);
|
||||
connect(input, &NodeInput::KeyframeAdded, this, &NodeParamViewItemBody::InputAddedKeyframe);
|
||||
connect(input, &NodeInput::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved);
|
||||
connect(conn.input, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewItemBody::InputKeyframeEnableChanged);
|
||||
connect(conn.input, &NodeInput::KeyframeAdded, this, &NodeParamViewItemBody::InputAddedKeyframe);
|
||||
connect(conn.input, &NodeInput::KeyframeRemoved, this, &NodeParamViewItemBody::KeyframeRemoved);
|
||||
}
|
||||
|
||||
input_ui_map_.insert(input, ui_objects);
|
||||
input_ui_map_.insert(conn, ui_objects);
|
||||
|
||||
// Update "connected" label
|
||||
if (input->is_connectable()) {
|
||||
UpdateUIForEdgeConnection(input);
|
||||
if (conn.input->IsConnectable()) {
|
||||
for (int i=-1; i<conn.input->ArraySize(); i++) {
|
||||
UpdateUIForEdgeConnection(conn.input, i);
|
||||
}
|
||||
}
|
||||
|
||||
row_count++;
|
||||
|
||||
// If the row count is an array, we put an extra body widget in the next row so we skip over it here
|
||||
if (input->IsArray()) {
|
||||
if (conn.input->IsArray()) {
|
||||
row_count++;
|
||||
}
|
||||
}
|
||||
@@ -336,10 +339,8 @@ void NodeParamViewItemBody::SetTime(const rational &time)
|
||||
|
||||
void NodeParamViewItemBody::Retranslate()
|
||||
{
|
||||
QMap<NodeInput*, InputUI>::const_iterator i;
|
||||
|
||||
for (i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) {
|
||||
i.value().main_label->setText(tr("%1:").arg(i.key()->name()));
|
||||
for (auto i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) {
|
||||
i.value().main_label->setText(tr("%1:").arg(i.key().input->name()));
|
||||
}
|
||||
|
||||
foreach (NodeParamViewItemBody* sb, sub_bodies_) {
|
||||
@@ -349,13 +350,11 @@ void NodeParamViewItemBody::Retranslate()
|
||||
|
||||
void NodeParamViewItemBody::SignalAllKeyframes()
|
||||
{
|
||||
QMap<NodeInput*, InputUI>::const_iterator i;
|
||||
for (auto i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) {
|
||||
NodeInput* input = i.key().input;
|
||||
|
||||
for (i=input_ui_map_.begin(); i!=input_ui_map_.end(); i++) {
|
||||
NodeInput* input = i.key();
|
||||
|
||||
foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) {
|
||||
foreach (NodeKeyframePtr key, track) {
|
||||
foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(i.key().element)) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
InputAddedKeyframeInternal(input, key);
|
||||
}
|
||||
}
|
||||
@@ -366,30 +365,32 @@ void NodeParamViewItemBody::SignalAllKeyframes()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::EdgeChanged()
|
||||
void NodeParamViewItemBody::EdgeChanged(Node* src, int element)
|
||||
{
|
||||
UpdateUIForEdgeConnection(static_cast<NodeInput*>(sender()));
|
||||
Q_UNUSED(src)
|
||||
|
||||
UpdateUIForEdgeConnection(static_cast<NodeInput*>(sender()), element);
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::UpdateUIForEdgeConnection(NodeInput *input)
|
||||
void NodeParamViewItemBody::UpdateUIForEdgeConnection(NodeInput *input, int element)
|
||||
{
|
||||
// Show/hide bridge widgets
|
||||
const InputUI& ui_objects = input_ui_map_[input];
|
||||
const InputUI& ui_objects = input_ui_map_[{input, element}];
|
||||
|
||||
foreach (QWidget* w, ui_objects.widget_bridge->widgets()) {
|
||||
w->setVisible(!input->is_connected());
|
||||
w->setVisible(!input->IsConnected(element));
|
||||
}
|
||||
|
||||
// Show/hide connection label
|
||||
ui_objects.connected_label->setVisible(input->is_connected());
|
||||
ui_objects.connected_label->setVisible(input->IsConnected(element));
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::InputKeyframeEnableChanged(bool e)
|
||||
void NodeParamViewItemBody::InputKeyframeEnableChanged(bool e, int element)
|
||||
{
|
||||
NodeInput* input = static_cast<NodeInput*>(sender());
|
||||
|
||||
foreach (const NodeInput::KeyframeTrack& track, input->keyframe_tracks()) {
|
||||
foreach (NodeKeyframePtr key, track) {
|
||||
foreach (const NodeKeyframeTrack& track, input->GetKeyframeTracks(element)) {
|
||||
foreach (NodeKeyframe* key, track) {
|
||||
if (e) {
|
||||
// Add a keyframe item for each keyframe
|
||||
InputAddedKeyframeInternal(input, key);
|
||||
@@ -401,7 +402,7 @@ void NodeParamViewItemBody::InputKeyframeEnableChanged(bool e)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframePtr key)
|
||||
void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframe* key)
|
||||
{
|
||||
// Get NodeInput that emitted this signal
|
||||
NodeInput* input = static_cast<NodeInput*>(sender());
|
||||
@@ -409,24 +410,11 @@ void NodeParamViewItemBody::InputAddedKeyframe(NodeKeyframePtr key)
|
||||
InputAddedKeyframeInternal(input, key);
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::LabelDoubleClicked()
|
||||
{
|
||||
QMap<NodeInput*, InputUI>::const_iterator iterator;
|
||||
|
||||
for (iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) {
|
||||
if (iterator.value().main_label == sender()) {
|
||||
emit InputDoubleClicked(iterator.key());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::ConnectionClicked()
|
||||
{
|
||||
QMap<NodeInput*, InputUI>::const_iterator iterator;
|
||||
for (iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) {
|
||||
for (auto iterator=input_ui_map_.begin(); iterator!=input_ui_map_.end(); iterator++) {
|
||||
if (iterator.value().connected_label == sender()) {
|
||||
Node* connected = iterator.key()->get_connected_node();
|
||||
Node* connected = iterator.key().input->parent();
|
||||
|
||||
if (connected) {
|
||||
emit RequestSelectNode({connected});
|
||||
@@ -437,7 +425,7 @@ void NodeParamViewItemBody::ConnectionClicked()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItemBody::InputAddedKeyframeInternal(NodeInput *input, NodeKeyframePtr keyframe)
|
||||
void NodeParamViewItemBody::InputAddedKeyframeInternal(NodeInput *input, NodeKeyframe* keyframe)
|
||||
{
|
||||
// Find its row in the parameters
|
||||
QLabel* lbl = input_ui_map_.value(input).main_label;
|
||||
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
class NodeParamViewItemBody : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeParamViewItemBody(const QVector<NodeInput*>& inputs, QWidget* parent = nullptr);
|
||||
NodeParamViewItemBody(const QVector<Node::InputConnection>& inputs, QWidget* parent = nullptr);
|
||||
|
||||
void SetTimeTarget(Node* target);
|
||||
|
||||
@@ -83,20 +83,18 @@ public:
|
||||
void SignalAllKeyframes();
|
||||
|
||||
signals:
|
||||
void KeyframeAdded(NodeKeyframePtr key, int y);
|
||||
void KeyframeAdded(NodeKeyframe* key, int y);
|
||||
|
||||
void KeyframeRemoved(NodeKeyframePtr key);
|
||||
void KeyframeRemoved(NodeKeyframe* key);
|
||||
|
||||
void RequestSetTime(const rational& time);
|
||||
|
||||
void InputDoubleClicked(NodeInput* input);
|
||||
|
||||
void RequestSelectNode(const QVector<Node*>& node);
|
||||
|
||||
private:
|
||||
void UpdateUIForEdgeConnection(NodeInput* input);
|
||||
void UpdateUIForEdgeConnection(NodeInput* input, int element);
|
||||
|
||||
void InputAddedKeyframeInternal(NodeInput* input, NodeKeyframePtr keyframe);
|
||||
void InputAddedKeyframeInternal(NodeInput* input, NodeKeyframe* keyframe);
|
||||
|
||||
struct InputUI {
|
||||
InputUI();
|
||||
@@ -107,18 +105,16 @@ private:
|
||||
NodeParamViewKeyframeControl* key_control;
|
||||
};
|
||||
|
||||
QMap<NodeInput*, InputUI> input_ui_map_;
|
||||
QHash<Node::InputConnection, InputUI> input_ui_map_;
|
||||
|
||||
QVector<NodeParamViewItemBody*> sub_bodies_;
|
||||
|
||||
private slots:
|
||||
void EdgeChanged();
|
||||
void EdgeChanged(Node *src, int element);
|
||||
|
||||
void InputKeyframeEnableChanged(bool e);
|
||||
void InputKeyframeEnableChanged(bool e, int element);
|
||||
|
||||
void InputAddedKeyframe(NodeKeyframePtr key);
|
||||
|
||||
void LabelDoubleClicked();
|
||||
void InputAddedKeyframe(NodeKeyframe* key);
|
||||
|
||||
void ConnectionClicked();
|
||||
|
||||
@@ -153,14 +149,12 @@ public slots:
|
||||
void ToggleExpanded();
|
||||
|
||||
signals:
|
||||
void KeyframeAdded(NodeKeyframePtr key, int y);
|
||||
void KeyframeAdded(NodeKeyframe* key, int y);
|
||||
|
||||
void KeyframeRemoved(NodeKeyframePtr key);
|
||||
void KeyframeRemoved(NodeKeyframe* key);
|
||||
|
||||
void RequestSetTime(const rational& time);
|
||||
|
||||
void InputDoubleClicked(NodeInput* input);
|
||||
|
||||
void RequestSelectNode(const QVector<Node*>& node);
|
||||
|
||||
void PinToggled(bool e);
|
||||
|
||||
@@ -67,16 +67,11 @@ NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWi
|
||||
connect(enable_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged);
|
||||
|
||||
// Set defaults
|
||||
SetInput(nullptr);
|
||||
SetInput(nullptr, -1);
|
||||
ShowButtonsFromKeyframeEnable(false);
|
||||
}
|
||||
|
||||
NodeInput *NodeParamViewKeyframeControl::GetConnectedInput() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetInput(NodeInput *input)
|
||||
void NodeParamViewKeyframeControl::SetInput(NodeInput *input, int element)
|
||||
{
|
||||
if (input_ != nullptr) {
|
||||
disconnect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked);
|
||||
@@ -85,10 +80,11 @@ void NodeParamViewKeyframeControl::SetInput(NodeInput *input)
|
||||
}
|
||||
|
||||
input_ = input;
|
||||
element_ = element;
|
||||
SetButtonsEnabled(input_);
|
||||
|
||||
// Pick up keyframing value
|
||||
enable_key_btn_->setChecked(input_ && input_->is_keyframing());
|
||||
enable_key_btn_->setChecked(input_ && input_->IsKeyframing(element_));
|
||||
|
||||
// Update buttons
|
||||
UpdateState();
|
||||
@@ -126,12 +122,12 @@ void NodeParamViewKeyframeControl::SetButtonsEnabled(bool e)
|
||||
|
||||
rational NodeParamViewKeyframeControl::GetCurrentTimeAsNodeTime() const
|
||||
{
|
||||
return GetAdjustedTime(GetTimeTarget(), input_->parentNode(), time_, NodeParam::kInput);
|
||||
return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true);
|
||||
}
|
||||
|
||||
rational NodeParamViewKeyframeControl::ConvertToViewerTime(const rational &r) const
|
||||
{
|
||||
return GetAdjustedTime(input_->parentNode(), GetTimeTarget(), r, NodeParam::kOutput);
|
||||
return GetAdjustedTime(input_->parent(), GetTimeTarget(), r, false);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable(bool e)
|
||||
@@ -145,30 +141,34 @@ void NodeParamViewKeyframeControl::ToggleKeyframe(bool e)
|
||||
{
|
||||
rational node_time = GetCurrentTimeAsNodeTime();
|
||||
|
||||
QList<NodeKeyframePtr> keys = input_->get_keyframe_at_time(node_time);
|
||||
QVector<NodeKeyframe*> keys = input_->GetKeyframesAtTime(node_time, element_);
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
int nb_tracks = input_->GetNumberOfKeyframeTracks();
|
||||
|
||||
if (e && keys.isEmpty()) {
|
||||
// Add a keyframe here (one for each track)
|
||||
for (int i=0;i<input_->get_number_of_keyframe_tracks();i++) {
|
||||
NodeKeyframePtr key = NodeKeyframe::Create(node_time,
|
||||
input_->get_value_at_time_for_track(node_time, i),
|
||||
input_->get_best_keyframe_type_for_time(node_time, i),
|
||||
i);
|
||||
for (int i=0;i<nb_tracks;i++) {
|
||||
NodeKeyframe* key = new NodeKeyframe(node_time,
|
||||
input_->GetValueAtTimeForTrack(node_time, i, element_),
|
||||
input_->GetBestKeyframeTypeForTime(node_time, i, element_),
|
||||
i,
|
||||
element_);
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input_, key, command);
|
||||
}
|
||||
} else if (!e && !keys.isEmpty()) {
|
||||
// Remove all keyframes at this time
|
||||
foreach (NodeKeyframePtr key, keys) {
|
||||
new NodeParamRemoveKeyframeCommand(input_, key, command);
|
||||
foreach (NodeKeyframe* key, keys) {
|
||||
new NodeParamRemoveKeyframeCommand(key, command);
|
||||
|
||||
if (input_->keyframe_tracks().at(key->track()).size() == 1) {
|
||||
if (input_->GetKeyframeTracks(key->track()).size() == 1) {
|
||||
// If this was the last keyframe on this track, set the standard value to the value at this time too
|
||||
new NodeParamSetStandardValueCommand(input_,
|
||||
key->track(),
|
||||
input_->get_value_at_time_for_track(node_time, key->track()),
|
||||
element_,
|
||||
input_->GetValueAtTimeForTrack(node_time, key->track(), element_),
|
||||
command);
|
||||
}
|
||||
}
|
||||
@@ -183,21 +183,21 @@ void NodeParamViewKeyframeControl::UpdateState()
|
||||
return;
|
||||
}
|
||||
|
||||
NodeKeyframePtr earliest_key = input_->get_earliest_keyframe();
|
||||
NodeKeyframePtr latest_key = input_->get_latest_keyframe();
|
||||
NodeKeyframe* earliest_key = input_->GetEarliestKeyframe(element_);
|
||||
NodeKeyframe* latest_key = input_->GetLatestKeyframe(element_);
|
||||
|
||||
rational node_time = GetCurrentTimeAsNodeTime();
|
||||
|
||||
prev_key_btn_->setEnabled(earliest_key && node_time > earliest_key->time());
|
||||
next_key_btn_->setEnabled(latest_key && node_time < latest_key->time());
|
||||
toggle_key_btn_->setChecked(input_->has_keyframe_at_time(node_time));
|
||||
toggle_key_btn_->setChecked(input_->HasKeyframeAtTime(node_time, element_));
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::GoToPreviousKey()
|
||||
{
|
||||
rational node_time = GetCurrentTimeAsNodeTime();
|
||||
|
||||
NodeKeyframePtr previous_key = input_->get_closest_keyframe_before_time(node_time);
|
||||
NodeKeyframe* previous_key = input_->GetClosestKeyframeBeforeTime(node_time, element_);
|
||||
|
||||
if (previous_key) {
|
||||
rational key_time = ConvertToViewerTime(previous_key->time());
|
||||
@@ -210,7 +210,7 @@ void NodeParamViewKeyframeControl::GoToNextKey()
|
||||
{
|
||||
rational node_time = GetCurrentTimeAsNodeTime();
|
||||
|
||||
NodeKeyframePtr next_key = input_->get_closest_keyframe_after_time(node_time);
|
||||
NodeKeyframe* next_key = input_->GetClosestKeyframeAfterTime(node_time, element_);
|
||||
|
||||
if (next_key) {
|
||||
rational key_time = ConvertToViewerTime(next_key->time());
|
||||
@@ -221,7 +221,7 @@ void NodeParamViewKeyframeControl::GoToNextKey()
|
||||
|
||||
void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e)
|
||||
{
|
||||
if (e == input_->is_keyframing()) {
|
||||
if (e == input_->IsKeyframing(element_)) {
|
||||
// No-op
|
||||
return;
|
||||
}
|
||||
@@ -233,13 +233,14 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e)
|
||||
new NodeParamSetKeyframingCommand(input_, true, command);
|
||||
|
||||
// Create one keyframe across all tracks here
|
||||
QVector<QVariant> key_vals = input_->get_split_standard_value();
|
||||
const QVector<QVariant>& key_vals = input_->GetSplitStandardValue(element_);
|
||||
|
||||
for (int i=0;i<key_vals.size();i++) {
|
||||
NodeKeyframePtr key = NodeKeyframe::Create(GetCurrentTimeAsNodeTime(),
|
||||
key_vals.at(i),
|
||||
NodeKeyframe::kDefaultType,
|
||||
i);
|
||||
NodeKeyframe* key = new NodeKeyframe(GetCurrentTimeAsNodeTime(),
|
||||
key_vals.at(i),
|
||||
NodeKeyframe::kDefaultType,
|
||||
i,
|
||||
element_);
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input_, key, command);
|
||||
}
|
||||
@@ -251,18 +252,18 @@ void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e)
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
|
||||
// Store value at this time, we'll set this as the persistent value later
|
||||
QVector<QVariant> stored_vals = input_->get_split_values_at_time(GetCurrentTimeAsNodeTime());
|
||||
const QVector<QVariant>& stored_vals = input_->GetSplitValuesAtTime(GetCurrentTimeAsNodeTime(), element_);
|
||||
|
||||
// Delete all keyframes
|
||||
foreach (const NodeInput::KeyframeTrack& track, input_->keyframe_tracks()) {
|
||||
foreach (const NodeKeyframeTrack& track, input_->GetKeyframeTracks(element_)) {
|
||||
for (int i=track.size()-1;i>=0;i--) {
|
||||
new NodeParamRemoveKeyframeCommand(input_, track.at(i), command);
|
||||
new NodeParamRemoveKeyframeCommand(track.at(i), command);
|
||||
}
|
||||
}
|
||||
|
||||
// Update standard value
|
||||
for (int i=0;i<stored_vals.size();i++) {
|
||||
new NodeParamSetStandardValueCommand(input_, i, stored_vals.at(i), command);
|
||||
new NodeParamSetStandardValueCommand(input_, i, element_, stored_vals.at(i), command);
|
||||
}
|
||||
|
||||
// Disable keyframing
|
||||
|
||||
@@ -35,9 +35,17 @@ class NodeParamViewKeyframeControl : public QWidget, public TimeTargetObject
|
||||
public:
|
||||
NodeParamViewKeyframeControl(bool right_align = true, QWidget* parent = nullptr);
|
||||
|
||||
NodeInput* GetConnectedInput() const;
|
||||
NodeInput* GetConnectedInput() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
|
||||
void SetInput(NodeInput* input);
|
||||
int GetConnectedElement() const
|
||||
{
|
||||
return element_;
|
||||
}
|
||||
|
||||
void SetInput(NodeInput* input, int element);
|
||||
|
||||
void SetTime(const rational& time);
|
||||
|
||||
@@ -59,6 +67,7 @@ private:
|
||||
QPushButton* enable_key_btn_;
|
||||
|
||||
NodeInput* input_;
|
||||
int element_;
|
||||
|
||||
rational time_;
|
||||
|
||||
|
||||
@@ -25,30 +25,31 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, bool setting, QUndoCommand *parent) :
|
||||
NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, int element, bool setting, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
input_(input),
|
||||
setting_(setting)
|
||||
setting_(setting),
|
||||
element_(element)
|
||||
{
|
||||
Q_ASSERT(setting != input_->is_keyframing());
|
||||
Q_ASSERT(setting != input_->IsKeyframing());
|
||||
}
|
||||
|
||||
Project *NodeParamSetKeyframingCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(input_->parentNode()->parent())->project();
|
||||
return static_cast<Sequence*>(input_->parent()->parent())->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframingCommand::redo_internal()
|
||||
{
|
||||
input_->set_is_keyframing(setting_);
|
||||
input_->SetIsKeyframing(setting_, element_);
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframingCommand::undo_internal()
|
||||
{
|
||||
input_->set_is_keyframing(!setting_);
|
||||
input_->SetIsKeyframing(!setting_, element_);
|
||||
}
|
||||
|
||||
NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent) :
|
||||
NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value, QUndoCommand* parent) :
|
||||
UndoCommand(parent),
|
||||
key_(key),
|
||||
old_value_(key_->value()),
|
||||
@@ -56,7 +57,7 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframeP
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) :
|
||||
NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
key_(key),
|
||||
old_value_(old_value),
|
||||
@@ -67,7 +68,7 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframeP
|
||||
|
||||
Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(key_->parent()->parentNode()->parent())->project();
|
||||
return static_cast<Sequence*>(key_->parent()->parent()->parent()->parent())->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeValueCommand::redo_internal()
|
||||
@@ -80,63 +81,53 @@ void NodeParamSetKeyframeValueCommand::undo_internal()
|
||||
key_->set_value(old_value_);
|
||||
}
|
||||
|
||||
NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand* parent) :
|
||||
NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframe* keyframe, QUndoCommand* parent) :
|
||||
UndoCommand(parent),
|
||||
input_(input),
|
||||
keyframe_(keyframe),
|
||||
done_(false)
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, bool already_done, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
input_(input),
|
||||
keyframe_(keyframe),
|
||||
done_(already_done)
|
||||
keyframe_(keyframe)
|
||||
{
|
||||
keyframe_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(input_->parentNode()->parent())->project();
|
||||
return static_cast<Sequence*>(input_->parent()->parent())->project();
|
||||
}
|
||||
|
||||
void NodeParamInsertKeyframeCommand::redo_internal()
|
||||
{
|
||||
if (!done_) {
|
||||
input_->insert_keyframe(keyframe_);
|
||||
}
|
||||
keyframe_->setParent(input_);
|
||||
}
|
||||
|
||||
void NodeParamInsertKeyframeCommand::undo_internal()
|
||||
{
|
||||
input_->remove_keyframe(keyframe_);
|
||||
done_ = false;
|
||||
keyframe_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeInput *input, NodeKeyframePtr keyframe, QUndoCommand *parent) :
|
||||
NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
input_(input),
|
||||
input_(keyframe->parent()),
|
||||
keyframe_(keyframe)
|
||||
{
|
||||
}
|
||||
|
||||
Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(input_->parentNode()->parent())->project();
|
||||
return static_cast<Sequence*>(input_->parent()->parent())->project();
|
||||
}
|
||||
|
||||
void NodeParamRemoveKeyframeCommand::redo_internal()
|
||||
{
|
||||
input_->remove_keyframe(keyframe_);
|
||||
// Removes from input
|
||||
keyframe_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
void NodeParamRemoveKeyframeCommand::undo_internal()
|
||||
{
|
||||
input_->insert_keyframe(keyframe_);
|
||||
keyframe_->setParent(input_);
|
||||
}
|
||||
|
||||
NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational &time, QUndoCommand *parent) :
|
||||
NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &time, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
key_(key),
|
||||
old_time_(key->time()),
|
||||
@@ -144,7 +135,7 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational &new_time, const rational &old_time, QUndoCommand *parent) :
|
||||
NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational &new_time, const rational &old_time, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
key_(key),
|
||||
old_time_(old_time),
|
||||
@@ -154,7 +145,7 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr
|
||||
|
||||
Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(key_->parent()->parentNode()->parent())->project();
|
||||
return static_cast<Sequence*>(key_->parent()->parent()->parent())->project();
|
||||
}
|
||||
|
||||
void NodeParamSetKeyframeTimeCommand::redo_internal()
|
||||
@@ -167,18 +158,20 @@ void NodeParamSetKeyframeTimeCommand::undo_internal()
|
||||
key_->set_time(old_time_);
|
||||
}
|
||||
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, const QVariant &value, QUndoCommand *parent) :
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &value, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
input_(input),
|
||||
element_(element),
|
||||
track_(track),
|
||||
old_value_(input_->get_standard_value()),
|
||||
old_value_(input_->GetStandardValue(element_)),
|
||||
new_value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) :
|
||||
NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *input, int track, int element, const QVariant &new_value, const QVariant &old_value, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
input_(input),
|
||||
element_(element),
|
||||
track_(track),
|
||||
old_value_(old_value),
|
||||
new_value_(new_value)
|
||||
@@ -187,17 +180,17 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *in
|
||||
|
||||
Project *NodeParamSetStandardValueCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(input_->parentNode()->parent())->project();
|
||||
return static_cast<Sequence*>(input_->parent()->parent())->project();
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::redo_internal()
|
||||
{
|
||||
input_->set_standard_value(new_value_, track_);
|
||||
input_->SetStandardValueOnTrack(new_value_, track_, element_);
|
||||
}
|
||||
|
||||
void NodeParamSetStandardValueCommand::undo_internal()
|
||||
{
|
||||
input_->set_standard_value(old_value_, track_);
|
||||
input_->SetStandardValueOnTrack(old_value_, track_, element_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace olive {
|
||||
|
||||
class NodeParamSetKeyframingCommand : public UndoCommand {
|
||||
public:
|
||||
NodeParamSetKeyframingCommand(NodeInput* input, bool setting, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframingCommand(NodeInput* input, int element, bool setting, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -39,12 +39,13 @@ protected:
|
||||
private:
|
||||
NodeInput* input_;
|
||||
bool setting_;
|
||||
int element_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamInsertKeyframeCommand : public UndoCommand {
|
||||
public:
|
||||
NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr);
|
||||
NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, bool already_done, QUndoCommand *parent = nullptr);
|
||||
NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframe* keyframe, QUndoCommand *parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -55,15 +56,15 @@ protected:
|
||||
private:
|
||||
NodeInput* input_;
|
||||
|
||||
NodeKeyframePtr keyframe_;
|
||||
NodeKeyframe* keyframe_;
|
||||
|
||||
bool done_;
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamRemoveKeyframeCommand : public UndoCommand {
|
||||
public:
|
||||
NodeParamRemoveKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr);
|
||||
NodeParamRemoveKeyframeCommand(NodeKeyframe* keyframe, QUndoCommand *parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -74,14 +75,16 @@ protected:
|
||||
private:
|
||||
NodeInput* input_;
|
||||
|
||||
NodeKeyframePtr keyframe_;
|
||||
NodeKeyframe* keyframe_;
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
};
|
||||
|
||||
class NodeParamSetKeyframeTimeCommand : public UndoCommand {
|
||||
public:
|
||||
NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational& time, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational& new_time, const rational& old_time, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& time, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframeTimeCommand(NodeKeyframe* key, const rational& new_time, const rational& old_time, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -90,7 +93,7 @@ protected:
|
||||
virtual void undo_internal() override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
NodeKeyframe* key_;
|
||||
|
||||
rational old_time_;
|
||||
rational new_time_;
|
||||
@@ -99,8 +102,8 @@ private:
|
||||
|
||||
class NodeParamSetKeyframeValueCommand : public UndoCommand {
|
||||
public:
|
||||
NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetKeyframeValueCommand(NodeKeyframe* key, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -109,7 +112,7 @@ protected:
|
||||
virtual void undo_internal() override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
NodeKeyframe* key_;
|
||||
|
||||
QVariant old_value_;
|
||||
QVariant new_value_;
|
||||
@@ -118,8 +121,8 @@ private:
|
||||
|
||||
class NodeParamSetStandardValueCommand : public UndoCommand {
|
||||
public:
|
||||
NodeParamSetStandardValueCommand(NodeInput* input, int track, const QVariant& value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetStandardValueCommand(NodeInput* input, int track, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& value, QUndoCommand* parent = nullptr);
|
||||
NodeParamSetStandardValueCommand(NodeInput* input, int track, int element, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -129,6 +132,7 @@ protected:
|
||||
|
||||
private:
|
||||
NodeInput* input_;
|
||||
int element_;
|
||||
int track_;
|
||||
|
||||
QVariant old_value_;
|
||||
|
||||
@@ -40,9 +40,10 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, QObject *parent) :
|
||||
NodeParamViewWidgetBridge::NodeParamViewWidgetBridge(NodeInput *input, int element, QObject *parent) :
|
||||
QObject(parent),
|
||||
input_(input)
|
||||
input_(input),
|
||||
element_(element)
|
||||
{
|
||||
CreateWidgets();
|
||||
|
||||
@@ -69,30 +70,24 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
|
||||
if (input_->IsArray()) {
|
||||
|
||||
NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(static_cast<NodeInputArray*>(input_));
|
||||
NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(input_);
|
||||
widgets_.append(w);
|
||||
|
||||
} else {
|
||||
|
||||
// We assume the first data type is the "primary" type
|
||||
switch (input_->data_type()) {
|
||||
switch (input_->GetDataType()) {
|
||||
// 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:
|
||||
case NodeParam::kShaderJob:
|
||||
case NodeParam::kSampleJob:
|
||||
case NodeParam::kGenerateJob:
|
||||
case NodeValue::kNone:
|
||||
case NodeValue::kTexture:
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kRational:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
break;
|
||||
case NodeParam::kInt:
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
IntegerSlider* slider = new IntegerSlider();
|
||||
slider->SetDefaultValue(input_->GetDefaultValue());
|
||||
@@ -101,27 +96,27 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFloat:
|
||||
case NodeValue::kFloat:
|
||||
{
|
||||
CreateSliders(1);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec2:
|
||||
case NodeValue::kVec2:
|
||||
{
|
||||
CreateSliders(2);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec3:
|
||||
case NodeValue::kVec3:
|
||||
{
|
||||
CreateSliders(3);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec4:
|
||||
case NodeValue::kVec4:
|
||||
{
|
||||
CreateSliders(4);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kCombo:
|
||||
case NodeValue::kCombo:
|
||||
{
|
||||
QComboBox* combobox = new QComboBox();
|
||||
|
||||
@@ -134,42 +129,42 @@ void NodeParamViewWidgetBridge::CreateWidgets()
|
||||
connect(combobox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFile:
|
||||
case NodeValue::kFile:
|
||||
// FIXME: File selector
|
||||
break;
|
||||
case NodeParam::kColor:
|
||||
case NodeValue::kColor:
|
||||
{
|
||||
// NOTE: Very convoluted way to get back to the project's color manager
|
||||
ColorButton* color_button = new ColorButton(static_cast<Sequence*>(input_->parentNode()->parent())->project()->color_manager());
|
||||
ColorButton* color_button = new ColorButton(input_->parent()->parent()->project()->color_manager());
|
||||
widgets_.append(color_button);
|
||||
connect(color_button, &ColorButton::ColorChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kText:
|
||||
case NodeValue::kText:
|
||||
{
|
||||
NodeParamViewRichText* line_edit = new NodeParamViewRichText();
|
||||
widgets_.append(line_edit);
|
||||
connect(line_edit, &NodeParamViewRichText::textEdited, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kBoolean:
|
||||
case NodeValue::kBoolean:
|
||||
{
|
||||
QCheckBox* check_box = new QCheckBox();
|
||||
widgets_.append(check_box);
|
||||
connect(check_box, &QCheckBox::clicked, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFont:
|
||||
case NodeValue::kFont:
|
||||
{
|
||||
QFontComboBox* font_combobox = new QFontComboBox();
|
||||
widgets_.append(font_combobox);
|
||||
connect(font_combobox, &QFontComboBox::currentFontChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFootage:
|
||||
case NodeValue::kFootage:
|
||||
{
|
||||
FootageComboBox* footage_combobox = new FootageComboBox();
|
||||
footage_combobox->SetRoot(static_cast<Sequence*>(input_->parentNode()->parent())->project()->root());
|
||||
footage_combobox->SetRoot(input_->parent()->parent()->project()->root());
|
||||
|
||||
connect(footage_combobox, &FootageComboBox::FootageChanged, this, &NodeParamViewWidgetBridge::WidgetCallback);
|
||||
|
||||
@@ -207,22 +202,23 @@ void NodeParamViewWidgetBridge::SetInputValueInternal(const QVariant &value, int
|
||||
{
|
||||
rational node_time = GetCurrentTimeAsNodeTime();
|
||||
|
||||
if (input_->is_keyframing()) {
|
||||
NodeKeyframePtr existing_key = input_->get_keyframe_at_time_on_track(node_time, track);
|
||||
if (input_->IsKeyframing(element_)) {
|
||||
NodeKeyframe* existing_key = input_->GetKeyframeAtTimeOnTrack(node_time, track, element_);
|
||||
|
||||
if (existing_key) {
|
||||
new NodeParamSetKeyframeValueCommand(existing_key, value, command);
|
||||
} else {
|
||||
// No existing key, create a new one
|
||||
NodeKeyframePtr new_key = NodeKeyframe::Create(node_time,
|
||||
value,
|
||||
input_->get_best_keyframe_type_for_time(node_time, track),
|
||||
track);
|
||||
NodeKeyframe* new_key = new NodeKeyframe(node_time,
|
||||
value,
|
||||
input_->GetBestKeyframeTypeForTime(node_time, track, element_),
|
||||
track,
|
||||
element_);
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input_, new_key, command);
|
||||
}
|
||||
} else {
|
||||
new NodeParamSetStandardValueCommand(input_, track, value, command);
|
||||
new NodeParamSetStandardValueCommand(input_, track, element_, value, command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,24 +253,18 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant
|
||||
|
||||
void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
{
|
||||
switch (input_->data_type()) {
|
||||
switch (input_->GetDataType()) {
|
||||
// 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:
|
||||
case NodeParam::kShaderJob:
|
||||
case NodeParam::kSampleJob:
|
||||
case NodeParam::kGenerateJob:
|
||||
case NodeValue::kNone:
|
||||
case NodeValue::kTexture:
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kRational:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
break;
|
||||
case NodeParam::kInt:
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
// Widget is a IntegerSlider
|
||||
IntegerSlider* slider = static_cast<IntegerSlider*>(sender());
|
||||
@@ -284,7 +274,7 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
ProcessSlider(slider, QVariant::fromValue(slider->GetValue() - offset));
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFloat:
|
||||
case NodeValue::kFloat:
|
||||
{
|
||||
// Widget is a FloatSlider
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
@@ -294,7 +284,7 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
ProcessSlider(slider, slider->GetValue() - offset);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec2:
|
||||
case NodeValue::kVec2:
|
||||
{
|
||||
// Widget is a FloatSlider
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
@@ -304,7 +294,7 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec3:
|
||||
case NodeValue::kVec3:
|
||||
{
|
||||
// Widget is a FloatSlider
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
@@ -314,7 +304,7 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec4:
|
||||
case NodeValue::kVec4:
|
||||
{
|
||||
// Widget is a FloatSlider
|
||||
FloatSlider* slider = static_cast<FloatSlider*>(sender());
|
||||
@@ -324,10 +314,10 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
ProcessSlider(slider, slider->GetValue() - offset[widgets_.indexOf(slider)]);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFile:
|
||||
case NodeValue::kFile:
|
||||
// FIXME: File selector
|
||||
break;
|
||||
case NodeParam::kColor:
|
||||
case NodeValue::kColor:
|
||||
{
|
||||
// Sender is a ColorButton
|
||||
ManagedColor c = static_cast<ColorButton*>(sender())->GetColor();
|
||||
@@ -349,31 +339,31 @@ void NodeParamViewWidgetBridge::WidgetCallback()
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kText:
|
||||
case NodeValue::kText:
|
||||
{
|
||||
// Sender is a NodeParamViewRichText
|
||||
SetInputValue(static_cast<NodeParamViewRichText*>(sender())->text(), 0);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kBoolean:
|
||||
case NodeValue::kBoolean:
|
||||
{
|
||||
// Widget is a QCheckBox
|
||||
SetInputValue(static_cast<QCheckBox*>(sender())->isChecked(), 0);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFont:
|
||||
case NodeValue::kFont:
|
||||
{
|
||||
// Widget is a QFontComboBox
|
||||
SetInputValue(static_cast<QFontComboBox*>(sender())->currentFont().family(), 0);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFootage:
|
||||
case NodeValue::kFootage:
|
||||
{
|
||||
// Widget is a FootageComboBox
|
||||
SetInputValue(QVariant::fromValue(static_cast<FootageComboBox*>(sender())->SelectedFootage()), 0);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kCombo:
|
||||
case NodeValue::kCombo:
|
||||
{
|
||||
// Widget is a QComboBox
|
||||
QComboBox* cb = static_cast<QComboBox*>(widgets_.first());
|
||||
@@ -412,49 +402,43 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
rational node_time = GetCurrentTimeAsNodeTime();
|
||||
|
||||
// We assume the first data type is the "primary" type
|
||||
switch (input_->data_type()) {
|
||||
switch (input_->GetDataType()) {
|
||||
// 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::kShaderJob:
|
||||
case NodeParam::kSampleJob:
|
||||
case NodeParam::kGenerateJob:
|
||||
case NodeParam::kVector:
|
||||
case NodeValue::kNone:
|
||||
case NodeValue::kTexture:
|
||||
case NodeValue::kMatrix:
|
||||
case NodeValue::kRational:
|
||||
case NodeValue::kSamples:
|
||||
case NodeValue::kShaderJob:
|
||||
case NodeValue::kSampleJob:
|
||||
case NodeValue::kGenerateJob:
|
||||
break;
|
||||
case NodeParam::kInt:
|
||||
case NodeValue::kInt:
|
||||
{
|
||||
int64_t offset = input_->property("offset").toLongLong();
|
||||
|
||||
static_cast<IntegerSlider*>(widgets_.first())->SetValue(input_->get_value_at_time(node_time).toLongLong() + offset);
|
||||
static_cast<IntegerSlider*>(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toLongLong() + offset);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFloat:
|
||||
case NodeValue::kFloat:
|
||||
{
|
||||
double offset = input_->property("offset").toDouble();
|
||||
|
||||
static_cast<FloatSlider*>(widgets_.first())->SetValue(input_->get_value_at_time(node_time).toDouble() + offset);
|
||||
static_cast<FloatSlider*>(widgets_.first())->SetValue(input_->GetValueAtTime(node_time, element_).toDouble() + offset);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec2:
|
||||
case NodeValue::kVec2:
|
||||
{
|
||||
QVector2D vec2 = input_->get_value_at_time(node_time).value<QVector2D>();
|
||||
QVector2D vec2 = input_->GetValueAtTime(node_time, -1).value<QVector2D>();
|
||||
QVector2D offset = input_->property("offset").value<QVector2D>();
|
||||
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->SetValue(static_cast<double>(vec2.x() + offset.x()));
|
||||
static_cast<FloatSlider*>(widgets_.at(1))->SetValue(static_cast<double>(vec2.y() + offset.y()));
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec3:
|
||||
case NodeValue::kVec3:
|
||||
{
|
||||
QVector3D vec3 = input_->get_value_at_time(node_time).value<QVector3D>();
|
||||
QVector3D vec3 = input_->GetValueAtTime(node_time, element_).value<QVector3D>();
|
||||
QVector3D offset = input_->property("offset").value<QVector3D>();
|
||||
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->SetValue(static_cast<double>(vec3.x() + offset.x()));
|
||||
@@ -462,9 +446,9 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
static_cast<FloatSlider*>(widgets_.at(2))->SetValue(static_cast<double>(vec3.z() + offset.z()));
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec4:
|
||||
case NodeValue::kVec4:
|
||||
{
|
||||
QVector4D vec4 = input_->get_value_at_time(node_time).value<QVector4D>();
|
||||
QVector4D vec4 = input_->GetValueAtTime(node_time, element_).value<QVector4D>();
|
||||
QVector4D offset = input_->property("offset").value<QVector4D>();
|
||||
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->SetValue(static_cast<double>(vec4.x() + offset.x()));
|
||||
@@ -473,12 +457,12 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
static_cast<FloatSlider*>(widgets_.at(3))->SetValue(static_cast<double>(vec4.w() + offset.w()));
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFile:
|
||||
case NodeValue::kFile:
|
||||
// FIXME: File selector
|
||||
break;
|
||||
case NodeParam::kColor:
|
||||
case NodeValue::kColor:
|
||||
{
|
||||
ManagedColor mc = input_->get_value_at_time(node_time).value<Color>();
|
||||
ManagedColor mc = input_->GetValueAtTime(node_time, element_).value<Color>();
|
||||
|
||||
mc.set_color_input(input_->property("col_input").toString());
|
||||
|
||||
@@ -491,40 +475,40 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues()
|
||||
static_cast<ColorButton*>(widgets_.first())->SetColor(mc);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kText:
|
||||
case NodeValue::kText:
|
||||
{
|
||||
NodeParamViewRichText* e = static_cast<NodeParamViewRichText*>(widgets_.first());
|
||||
e->setTextPreservingCursor(input_->get_value_at_time(node_time).toString());
|
||||
e->setTextPreservingCursor(input_->GetValueAtTime(node_time, element_).toString());
|
||||
break;
|
||||
}
|
||||
case NodeParam::kBoolean:
|
||||
static_cast<QCheckBox*>(widgets_.first())->setChecked(input_->get_value_at_time(node_time).toBool());
|
||||
case NodeValue::kBoolean:
|
||||
static_cast<QCheckBox*>(widgets_.first())->setChecked(input_->GetValueAtTime(node_time, element_).toBool());
|
||||
break;
|
||||
case NodeParam::kFont:
|
||||
case NodeValue::kFont:
|
||||
{
|
||||
QFontComboBox* fc = static_cast<QFontComboBox*>(widgets_.first());
|
||||
fc->blockSignals(true);
|
||||
fc->setCurrentFont(input_->get_value_at_time(node_time).toString());
|
||||
fc->setCurrentFont(input_->GetValueAtTime(node_time, element_).toString());
|
||||
fc->blockSignals(false);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kCombo:
|
||||
case NodeValue::kCombo:
|
||||
{
|
||||
QComboBox* cb = static_cast<QComboBox*>(widgets_.first());
|
||||
cb->blockSignals(true);
|
||||
cb->setCurrentIndex(input_->get_value_at_time(node_time).toInt());
|
||||
cb->setCurrentIndex(input_->GetValueAtTime(node_time, element_).toInt());
|
||||
cb->blockSignals(false);
|
||||
break;
|
||||
}
|
||||
case NodeParam::kFootage:
|
||||
static_cast<FootageComboBox*>(widgets_.first())->SetFootage(Node::ValueToPtr<Stream>(input_->get_value_at_time(node_time)));
|
||||
case NodeValue::kFootage:
|
||||
static_cast<FootageComboBox*>(widgets_.first())->SetFootage(Node::ValueToPtr<Stream>(input_->GetValueAtTime(node_time, element_)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rational NodeParamViewWidgetBridge::GetCurrentTimeAsNodeTime() const
|
||||
{
|
||||
return GetAdjustedTime(GetTimeTarget(), input_->parentNode(), time_, NodeParam::kInput);
|
||||
return GetAdjustedTime(GetTimeTarget(), input_->parent(), time_, true);
|
||||
}
|
||||
|
||||
void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range)
|
||||
@@ -538,7 +522,7 @@ void NodeParamViewWidgetBridge::InputValueChanged(const TimeRange &range)
|
||||
void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVariant &value)
|
||||
{
|
||||
// Parameters for vectors only
|
||||
if (input_->data_type() & NodeParam::kVector) {
|
||||
if (NodeValue::type_is_vector(input_->GetDataType())) {
|
||||
if (key == QStringLiteral("disablex")) {
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->setEnabled(!value.toBool());
|
||||
} else if (key == QStringLiteral("disabley")) {
|
||||
@@ -551,26 +535,26 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria
|
||||
}
|
||||
|
||||
// Parameters for integers, floats, and vectors
|
||||
if (input_->data_type() & NodeParam::kNumber || input_->data_type() & NodeParam::kVector) {
|
||||
if (NodeValue::type_is_numeric(input_->GetDataType()) || NodeValue::type_is_vector(input_->GetDataType())) {
|
||||
if (key == QStringLiteral("min")) {
|
||||
switch (input_->data_type()) {
|
||||
case NodeParam::kInt:
|
||||
switch (input_->GetDataType()) {
|
||||
case NodeValue::kInt:
|
||||
static_cast<IntegerSlider*>(widgets_.first())->SetMinimum(value.value<int64_t>());
|
||||
break;
|
||||
case NodeParam::kFloat:
|
||||
case NodeValue::kFloat:
|
||||
static_cast<FloatSlider*>(widgets_.first())->SetMinimum(value.toDouble());
|
||||
break;
|
||||
case NodeParam::kRational:
|
||||
case NodeValue::kRational:
|
||||
// FIXME: Rational doesn't have a UI implementation yet
|
||||
break;
|
||||
case NodeParam::kVec2:
|
||||
case NodeValue::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:
|
||||
case NodeValue::kVec3:
|
||||
{
|
||||
QVector3D min = value.value<QVector3D>();
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->SetMinimum(min.x());
|
||||
@@ -578,7 +562,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria
|
||||
static_cast<FloatSlider*>(widgets_.at(2))->SetMinimum(min.z());
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec4:
|
||||
case NodeValue::kVec4:
|
||||
{
|
||||
QVector4D min = value.value<QVector4D>();
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->SetMinimum(min.x());
|
||||
@@ -591,24 +575,24 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria
|
||||
break;
|
||||
}
|
||||
} else if (key == QStringLiteral("max")) {
|
||||
switch (input_->data_type()) {
|
||||
case NodeParam::kInt:
|
||||
switch (input_->GetDataType()) {
|
||||
case NodeValue::kInt:
|
||||
static_cast<IntegerSlider*>(widgets_.first())->SetMaximum(value.value<int64_t>());
|
||||
break;
|
||||
case NodeParam::kFloat:
|
||||
case NodeValue::kFloat:
|
||||
static_cast<FloatSlider*>(widgets_.first())->SetMaximum(value.toDouble());
|
||||
break;
|
||||
case NodeParam::kRational:
|
||||
case NodeValue::kRational:
|
||||
// FIXME: Rational doesn't have a UI implementation yet
|
||||
break;
|
||||
case NodeParam::kVec2:
|
||||
case NodeValue::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:
|
||||
case NodeValue::kVec3:
|
||||
{
|
||||
QVector3D max = value.value<QVector3D>();
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->SetMaximum(max.x());
|
||||
@@ -616,7 +600,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria
|
||||
static_cast<FloatSlider*>(widgets_.at(2))->SetMaximum(max.z());
|
||||
break;
|
||||
}
|
||||
case NodeParam::kVec4:
|
||||
case NodeValue::kVec4:
|
||||
{
|
||||
QVector4D max = value.value<QVector4D>();
|
||||
static_cast<FloatSlider*>(widgets_.at(0))->SetMaximum(max.x());
|
||||
@@ -634,7 +618,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria
|
||||
}
|
||||
|
||||
// ComboBox strings changing
|
||||
if (input_->data_type() & NodeParam::kCombo) {
|
||||
if (input_->GetDataType() & NodeValue::kCombo) {
|
||||
QComboBox* cb = static_cast<QComboBox*>(widgets_.first());
|
||||
|
||||
int old_index = cb->currentIndex();
|
||||
@@ -665,7 +649,7 @@ void NodeParamViewWidgetBridge::PropertyChanged(const QString &key, const QVaria
|
||||
}
|
||||
|
||||
// Parameters for floats and vectors only
|
||||
if (input_->data_type() & NodeParam::kFloat || input_->data_type() & NodeParam::kVector) {
|
||||
if (input_->GetDataType() == NodeValue::kFloat || NodeValue::type_is_vector(input_->GetDataType())) {
|
||||
if (key == QStringLiteral("view")) {
|
||||
FloatSlider::DisplayType display_type;
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class NodeParamViewWidgetBridge : public QObject, public TimeTargetObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeParamViewWidgetBridge(NodeInput* input, QObject* parent);
|
||||
NodeParamViewWidgetBridge(NodeInput* input, int element, QObject* parent);
|
||||
|
||||
void SetTime(const rational& time);
|
||||
|
||||
@@ -64,6 +64,8 @@ private:
|
||||
|
||||
NodeInput* input_;
|
||||
|
||||
int element_;
|
||||
|
||||
QList<QWidget*> widgets_;
|
||||
|
||||
rational time_;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <QCheckBox>
|
||||
#include <QHeaderView>
|
||||
|
||||
#include "node/param.h"
|
||||
#include "nodetabletraverser.h"
|
||||
|
||||
namespace olive {
|
||||
@@ -130,7 +129,7 @@ void NodeTableView::SetTime(const rational &time)
|
||||
QTreeWidgetItem* sub_item = input_item->child(j);
|
||||
|
||||
// Set data type name
|
||||
sub_item->setText(0, NodeParam::GetPrettyDataTypeName(value.type()));
|
||||
sub_item->setText(0, NodeValue::GetPrettyDataTypeName(value.type()));
|
||||
|
||||
// Determine source
|
||||
QString source_name;
|
||||
@@ -142,7 +141,7 @@ void NodeTableView::SetTime(const rational &time)
|
||||
sub_item->setText(1, source_name);
|
||||
|
||||
switch (value.type()) {
|
||||
case NodeParam::kTexture:
|
||||
case NodeValue::kTexture:
|
||||
{
|
||||
// NodeTableTraverser puts video params in here
|
||||
for (int k=0;k<VideoParams::kRGBAChannelCount;k++) {
|
||||
@@ -152,9 +151,9 @@ void NodeTableView::SetTime(const rational &time)
|
||||
}
|
||||
default:
|
||||
{
|
||||
QVector<QVariant> split_values = input->split_normal_value_into_track_values(value.data());
|
||||
QVector<QVariant> split_values = NodeValue::split_normal_value_into_track_values(input->GetDataType(), value.data());
|
||||
for (int k=0;k<split_values.size();k++) {
|
||||
sub_item->setText(2 + k, NodeInput::ValueToString(value.type(), split_values.at(k), true));
|
||||
sub_item->setText(2 + k, NodeValue::ValueToString(value.type(), split_values.at(k), true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,9 +34,8 @@ void NodeTreeView::SetNodes(const QVector<Node *> &nodes)
|
||||
node_item->setData(0, kItemType, kItemTypeNode);
|
||||
node_item->setData(0, kItemPointer, reinterpret_cast<quintptr>(n));
|
||||
|
||||
QVector<NodeInput*> inputs = n->GetInputsIncludingArrays();
|
||||
foreach (NodeInput* i, inputs) {
|
||||
if (only_show_keyframable_ && !i->is_keyframable()) {
|
||||
foreach (NodeInput* i, n->inputs()) {
|
||||
if (only_show_keyframable_ && !i->IsKeyframable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,18 +16,9 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/nodeview/nodeview.h
|
||||
widget/nodeview/nodeview.cpp
|
||||
widget/nodeview/nodeviewcommon.h
|
||||
widget/nodeview/nodeviewedge.h
|
||||
widget/nodeview/nodeviewedge.cpp
|
||||
widget/nodeview/nodeviewitem.h
|
||||
widget/nodeview/nodeviewitem.cpp
|
||||
widget/nodeview/nodeviewitemwidgetproxy.h
|
||||
widget/nodeview/nodeviewitemwidgetproxy.cpp
|
||||
widget/nodeview/nodeviewscene.h
|
||||
widget/nodeview/nodeviewscene.cpp
|
||||
widget/nodeview/nodeviewundo.h
|
||||
widget/nodeview/nodeview.h
|
||||
widget/nodeview/nodeviewundo.cpp
|
||||
widget/nodeview/nodeviewundo.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -20,887 +20,14 @@
|
||||
|
||||
#include "nodeview.h"
|
||||
|
||||
#include <QInputDialog>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include "core.h"
|
||||
#include "nodeviewundo.h"
|
||||
#include "node/factory.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
|
||||
#define super HandMovableView
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeView::NodeView(QWidget *parent) :
|
||||
NodeView::NodeView(QWidget* parent) :
|
||||
HandMovableView(parent),
|
||||
graph_(nullptr),
|
||||
drop_edge_(nullptr),
|
||||
filter_mode_(kFilterShowSelectedBlocks),
|
||||
scale_(1.0)
|
||||
graph_(nullptr)
|
||||
{
|
||||
setScene(&scene_);
|
||||
SetDefaultDragMode(RubberBandDrag);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setMouseTracking(true);
|
||||
setRenderHint(QPainter::Antialiasing);
|
||||
setViewportUpdateMode(FullViewportUpdate);
|
||||
|
||||
connect(&scene_, &QGraphicsScene::changed, this, &NodeView::ItemsChanged);
|
||||
connect(this, &NodeView::customContextMenuRequested, this, &NodeView::ShowContextMenu);
|
||||
|
||||
ConnectSelectionChangedSignal();
|
||||
|
||||
SetFlowDirection(NodeViewCommon::kTopToBottom);
|
||||
|
||||
// Set massive scene rect and hide the scrollbars to create an "infinite space" effect
|
||||
scene_.setSceneRect(-1000000, -1000000, 2000000, 2000000);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
|
||||
select_blocks_internal_timer_.setSingleShot(true);
|
||||
select_blocks_internal_timer_.setInterval(100);
|
||||
connect(&select_blocks_internal_timer_, &QTimer::timeout, this, &NodeView::SelectBlocksInternal);
|
||||
}
|
||||
|
||||
NodeView::~NodeView()
|
||||
{
|
||||
// Unset the current graph
|
||||
SetGraph(nullptr);
|
||||
}
|
||||
|
||||
void NodeView::SetGraph(NodeGraph *graph)
|
||||
{
|
||||
if (graph_ == graph) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (graph_ != nullptr) {
|
||||
disconnect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode);
|
||||
disconnect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode);
|
||||
disconnect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::GraphNodeRemoved);
|
||||
disconnect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded);
|
||||
disconnect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved);
|
||||
}
|
||||
|
||||
// Clear the scene of all UI objects
|
||||
scene_.clear();
|
||||
|
||||
// Set reference to the graph
|
||||
graph_ = graph;
|
||||
scene_.SetGraph(graph_);
|
||||
|
||||
// If the graph is valid, add UI objects for each of its Nodes
|
||||
if (graph_ != nullptr) {
|
||||
connect(graph_, &NodeGraph::NodeAdded, &scene_, &NodeViewScene::AddNode);
|
||||
connect(graph_, &NodeGraph::NodeRemoved, &scene_, &NodeViewScene::RemoveNode);
|
||||
connect(graph_, &NodeGraph::NodeRemoved, this, &NodeView::GraphNodeRemoved);
|
||||
connect(graph_, &NodeGraph::EdgeAdded, this, &NodeView::GraphEdgeAdded);
|
||||
connect(graph_, &NodeGraph::EdgeRemoved, this, &NodeView::GraphEdgeRemoved);
|
||||
|
||||
foreach (Node* n, graph_->nodes()) {
|
||||
scene_.AddNode(n);
|
||||
}
|
||||
|
||||
ValidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::DeleteSelected()
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
{
|
||||
QVector<NodeEdge*> selected_edges = scene_.GetSelectedEdges();
|
||||
|
||||
foreach (NodeEdge* edge, selected_edges) {
|
||||
new NodeEdgeRemoveCommand(edge->output(), edge->input(), command);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
QVector<Node*> selected_nodes = scene_.GetSelectedNodes();
|
||||
|
||||
// Ensure no nodes are "undeletable"
|
||||
for (int i=0;i<selected_nodes.size();i++) {
|
||||
if (!selected_nodes.at(i)->CanBeDeleted()) {
|
||||
selected_nodes.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected_nodes.isEmpty()) {
|
||||
new NodeRemoveCommand(graph_, selected_nodes, command);
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void NodeView::SelectAll()
|
||||
{
|
||||
// Optimization: rather than respond to every single item being selected, ignore the signal and
|
||||
// then handle them all at the end.
|
||||
DisconnectSelectionChangedSignal();
|
||||
|
||||
scene_.SelectAll();
|
||||
|
||||
ConnectSelectionChangedSignal();
|
||||
SceneSelectionChangedSlot();
|
||||
}
|
||||
|
||||
void NodeView::DeselectAll()
|
||||
{
|
||||
// Optimization: rather than respond to every single item being selected, ignore the signal and
|
||||
// then handle them all at the end.
|
||||
DisconnectSelectionChangedSignal();
|
||||
|
||||
scene_.DeselectAll();
|
||||
|
||||
ConnectSelectionChangedSignal();
|
||||
SceneSelectionChangedSlot();
|
||||
}
|
||||
|
||||
void NodeView::Select(const QVector<Node *> &nodes)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimization: rather than respond to every single item being selected, ignore the signal and
|
||||
// then handle them all at the end.
|
||||
DisconnectSelectionChangedSignal();
|
||||
|
||||
scene_.DeselectAll();
|
||||
|
||||
foreach (Node* n, nodes) {
|
||||
NodeViewItem* item = scene_.NodeToUIObject(n);
|
||||
|
||||
item->setSelected(true);
|
||||
}
|
||||
|
||||
ConnectSelectionChangedSignal();
|
||||
SceneSelectionChangedSlot();
|
||||
}
|
||||
|
||||
void NodeView::SelectWithDependencies(QVector<Node *> nodes)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int original_length = nodes.size();
|
||||
for (int i=0;i<original_length;i++) {
|
||||
nodes.append(nodes.at(i)->GetDependencies());
|
||||
}
|
||||
|
||||
Select(nodes);
|
||||
}
|
||||
|
||||
void NodeView::SelectBlocks(const QVector<Block *> &blocks)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
selected_blocks_.append(blocks);
|
||||
|
||||
QueueSelectBlocksInternal();
|
||||
}
|
||||
|
||||
void NodeView::DeselectBlocks(const QVector<Block *> &blocks)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove temporary associations
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
if (!blocks.contains(b)) {
|
||||
temporary_association_map_.remove(b);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove blocks from selected array
|
||||
foreach (Block* b, blocks) {
|
||||
selected_blocks_.removeOne(b);
|
||||
}
|
||||
|
||||
QueueSelectBlocksInternal();
|
||||
}
|
||||
|
||||
void NodeView::CopySelected(bool cut)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<Node*> selected = scene_.GetSelectedNodes();
|
||||
|
||||
if (selected.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CopyNodesToClipboard(selected);
|
||||
|
||||
if (cut) {
|
||||
DeleteSelected();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::Paste()
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
QVector<Node*> pasted_nodes = PasteNodesFromClipboard(static_cast<Sequence*>(graph_), command);
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
if (!pasted_nodes.isEmpty()) {
|
||||
foreach (Node* n, pasted_nodes) {
|
||||
AssociateNodeWithSelectedBlocks(n);
|
||||
}
|
||||
|
||||
AttachNodesToCursor(pasted_nodes);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::Duplicate()
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<Node*> selected = scene_.GetSelectedNodes();
|
||||
|
||||
if (selected.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
QVector<Node*> duplicated_nodes = Node::CopyDependencyGraph(selected, command);
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
|
||||
AttachNodesToCursor(duplicated_nodes);
|
||||
}
|
||||
|
||||
void NodeView::ItemsChanged()
|
||||
{
|
||||
QHash<NodeEdge *, NodeViewEdge *>::const_iterator i;
|
||||
|
||||
for (i=scene_.edge_map().constBegin(); i!=scene_.edge_map().constEnd(); i++) {
|
||||
i.value()->Adjust();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
super::keyPressEvent(event);
|
||||
|
||||
if (event->key() == Qt::Key_Escape && !attached_items_.isEmpty()) {
|
||||
DetachItemsFromCursor();
|
||||
|
||||
// We undo the last action which SHOULD be adding the node
|
||||
// FIXME: Possible danger of this not being the case?
|
||||
Core::instance()->undo_stack()->undo();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (HandPress(event)) return;
|
||||
|
||||
if (event->button() == Qt::RightButton) {
|
||||
// Qt doesn't do this by default for some reason
|
||||
if (!(event->modifiers() & Qt::ShiftModifier)) {
|
||||
scene_.clearSelection();
|
||||
}
|
||||
|
||||
// If there's an item here, select it
|
||||
QGraphicsItem* item = itemAt(event->pos());
|
||||
if (item) {
|
||||
item->setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
super::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void NodeView::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (HandMove(event)) return;
|
||||
|
||||
super::mouseMoveEvent(event);
|
||||
|
||||
if (!attached_items_.isEmpty()) {
|
||||
MoveAttachedNodesToCursor(event->pos());
|
||||
|
||||
// See if the user clicked on an edge (only when dropping single nodes)
|
||||
if (attached_items_.size() == 1) {
|
||||
Node* attached_node = attached_items_.first().item->GetNode();
|
||||
|
||||
QRect edge_detect_rect(event->pos(), event->pos());
|
||||
|
||||
int edge_detect_radius = fontMetrics().height();
|
||||
edge_detect_rect.adjust(-edge_detect_radius, -edge_detect_radius, edge_detect_radius, edge_detect_radius);
|
||||
|
||||
QList<QGraphicsItem*> items = this->items(edge_detect_rect);
|
||||
|
||||
NodeViewEdge* new_drop_edge = nullptr;
|
||||
|
||||
// See if there is an edge here
|
||||
foreach (QGraphicsItem* item, items) {
|
||||
new_drop_edge = dynamic_cast<NodeViewEdge*>(item);
|
||||
|
||||
if (new_drop_edge) {
|
||||
drop_input_ = nullptr;
|
||||
|
||||
foreach (NodeParam* param, attached_node->parameters()) {
|
||||
if (param->type() == NodeParam::kInput) {
|
||||
NodeInput* input = static_cast<NodeInput*>(param);
|
||||
|
||||
if (input->is_connectable()) {
|
||||
if (input->data_type() & new_drop_edge->edge()->input()->data_type()) {
|
||||
drop_input_ = input;
|
||||
break;
|
||||
} else if (!drop_input_) {
|
||||
drop_input_ = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drop_input_) {
|
||||
break;
|
||||
} else {
|
||||
new_drop_edge = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drop_edge_ != new_drop_edge) {
|
||||
if (drop_edge_) {
|
||||
drop_edge_->SetHighlighted(false);
|
||||
}
|
||||
|
||||
drop_edge_ = new_drop_edge;
|
||||
|
||||
if (drop_edge_) {
|
||||
drop_edge_->SetHighlighted(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (HandRelease(event)) return;
|
||||
|
||||
if (!attached_items_.isEmpty()) {
|
||||
if (attached_items_.size() == 1) {
|
||||
Node* dropping_node = attached_items_.first().item->GetNode();
|
||||
|
||||
if (drop_edge_) {
|
||||
NodeEdgePtr old_edge = drop_edge_->edge();
|
||||
|
||||
// We have everything we need to place the node in between
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
// Remove old edge
|
||||
new NodeEdgeRemoveCommand(old_edge, command);
|
||||
|
||||
// Place new edges
|
||||
new NodeEdgeAddCommand(old_edge->output(), drop_input_, command);
|
||||
new NodeEdgeAddCommand(dropping_node->output(), old_edge->input(), command);
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
drop_edge_ = nullptr;
|
||||
}
|
||||
|
||||
DetachItemsFromCursor();
|
||||
}
|
||||
|
||||
super::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void NodeView::wheelEvent(QWheelEvent *event)
|
||||
{
|
||||
if (event->modifiers() & Qt::ControlModifier) {
|
||||
// FIXME: Hardcoded divider (0.001)
|
||||
qreal multiplier = 1.0 + (static_cast<qreal>(event->angleDelta().x() + event->angleDelta().y()) * 0.001);
|
||||
|
||||
double test_scale = scale_ * multiplier;
|
||||
|
||||
if (test_scale > 0.1) {
|
||||
scale(multiplier, multiplier);
|
||||
scale_ = test_scale;
|
||||
}
|
||||
} else {
|
||||
QWidget::wheelEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::SceneSelectionChangedSlot()
|
||||
{
|
||||
QVector<Node*> current_selection = scene_.GetSelectedNodes();
|
||||
|
||||
QVector<Node*> selected;
|
||||
QVector<Node*> deselected;
|
||||
|
||||
// Determine which nodes are newly selected
|
||||
if (selected_nodes_.isEmpty()) {
|
||||
// All nodes in the current selection have just been selected
|
||||
selected = current_selection;
|
||||
} else {
|
||||
foreach (Node* n, current_selection) {
|
||||
if (!selected_nodes_.contains(n)) {
|
||||
selected.append(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which nodes are newly deselected
|
||||
if (current_selection.isEmpty()) {
|
||||
// All nodes that were selected have been deselected
|
||||
deselected = selected_nodes_;
|
||||
} else {
|
||||
foreach (Node* n, selected_nodes_) {
|
||||
if (!current_selection.contains(n)) {
|
||||
deselected.append(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selected_nodes_ = current_selection;
|
||||
|
||||
if (!selected.isEmpty()) {
|
||||
emit NodesSelected(selected);
|
||||
}
|
||||
|
||||
if (!deselected.isEmpty()) {
|
||||
emit NodesDeselected(deselected);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::ShowContextMenu(const QPoint &pos)
|
||||
{
|
||||
if (!graph_) {
|
||||
return;
|
||||
}
|
||||
|
||||
Menu m;
|
||||
|
||||
MenuShared::instance()->AddItemsForEditMenu(&m, false);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
QVector<NodeViewItem*> selected = scene_.GetSelectedItems();
|
||||
|
||||
if (itemAt(pos) && !selected.isEmpty()) {
|
||||
|
||||
// Label node action
|
||||
QAction* label_action = m.addAction(tr("Label"));
|
||||
connect(label_action, &QAction::triggered, this, [this](){
|
||||
Core::instance()->LabelNodes(scene_.GetSelectedNodes());
|
||||
});
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
// Auto-position action
|
||||
QAction* autopos = m.addAction(tr("Auto-Position"));
|
||||
connect(autopos, &QAction::triggered, this, &NodeView::AutoPositionDescendents);
|
||||
|
||||
} else {
|
||||
|
||||
QAction* curved_action = m.addAction(tr("Smooth Edges"));
|
||||
curved_action->setCheckable(true);
|
||||
curved_action->setChecked(scene_.GetEdgesAreCurved());
|
||||
connect(curved_action, &QAction::triggered, &scene_, &NodeViewScene::SetEdgesAreCurved);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
Menu* filter_menu = new Menu(tr("Filter"), &m);
|
||||
m.addMenu(filter_menu);
|
||||
|
||||
filter_menu->AddActionWithData(tr("Show All"),
|
||||
kFilterShowAll,
|
||||
filter_mode_);
|
||||
|
||||
filter_menu->AddActionWithData(tr("Show Selected Blocks Only"),
|
||||
kFilterShowSelectedBlocks,
|
||||
filter_mode_);
|
||||
|
||||
connect(filter_menu, &Menu::triggered, this, &NodeView::ContextMenuFilterChanged);
|
||||
|
||||
|
||||
|
||||
Menu* direction_menu = new Menu(tr("Direction"), &m);
|
||||
m.addMenu(direction_menu);
|
||||
|
||||
direction_menu->AddActionWithData(tr("Top to Bottom"),
|
||||
NodeViewCommon::kTopToBottom,
|
||||
scene_.GetFlowDirection());
|
||||
|
||||
direction_menu->AddActionWithData(tr("Bottom to Top"),
|
||||
NodeViewCommon::kBottomToTop,
|
||||
scene_.GetFlowDirection());
|
||||
|
||||
direction_menu->AddActionWithData(tr("Left to Right"),
|
||||
NodeViewCommon::kLeftToRight,
|
||||
scene_.GetFlowDirection());
|
||||
|
||||
direction_menu->AddActionWithData(tr("Right to Left"),
|
||||
NodeViewCommon::kRightToLeft,
|
||||
scene_.GetFlowDirection());
|
||||
|
||||
connect(direction_menu, &Menu::triggered, this, &NodeView::ContextMenuSetDirection);
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
Menu* add_menu = NodeFactory::CreateMenu(&m);
|
||||
add_menu->setTitle(tr("Add"));
|
||||
connect(add_menu, &Menu::triggered, this, &NodeView::CreateNodeSlot);
|
||||
m.addMenu(add_menu);
|
||||
|
||||
}
|
||||
|
||||
m.exec(mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void NodeView::CreateNodeSlot(QAction *action)
|
||||
{
|
||||
Node* new_node = NodeFactory::CreateFromMenuAction(action);
|
||||
|
||||
if (new_node) {
|
||||
// Associate this new node with these blocks (allows it to be visible with them even while it
|
||||
// isn't connected to anything)
|
||||
AssociateNodeWithSelectedBlocks(new_node);
|
||||
|
||||
Core::instance()->undo_stack()->push(new NodeAddCommand(graph_, new_node));
|
||||
|
||||
NodeViewItem* item = scene_.NodeToUIObject(new_node);
|
||||
AttachItemsToCursor({item});
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::ContextMenuSetDirection(QAction *action)
|
||||
{
|
||||
SetFlowDirection(static_cast<NodeViewCommon::FlowDirection>(action->data().toInt()));
|
||||
}
|
||||
|
||||
void NodeView::AutoPositionDescendents()
|
||||
{
|
||||
QVector<Node*> selected = scene_.GetSelectedNodes();
|
||||
|
||||
foreach (Node* n, selected) {
|
||||
scene_.ReorganizeFrom(n);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::ContextMenuFilterChanged(QAction *action)
|
||||
{
|
||||
FilterMode filter = static_cast<FilterMode>(action->data().toInt());
|
||||
|
||||
if (filter_mode_ != filter) {
|
||||
filter_mode_ = filter;
|
||||
|
||||
if (filter == kFilterShowAll) {
|
||||
// Un-hide all blocks
|
||||
foreach (NodeViewItem* item, scene_.item_map()) {
|
||||
item->setVisible(true);
|
||||
}
|
||||
|
||||
foreach (NodeViewEdge* edge, scene_.edge_map()) {
|
||||
edge->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
ValidateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::AttachNodesToCursor(const QVector<Node *> &nodes)
|
||||
{
|
||||
QVector<NodeViewItem*> items(nodes.size());
|
||||
|
||||
for (int i=0; i<nodes.size(); i++) {
|
||||
items[i] = scene_.NodeToUIObject(nodes.at(i));
|
||||
}
|
||||
|
||||
AttachItemsToCursor(items);
|
||||
}
|
||||
|
||||
void NodeView::AttachItemsToCursor(const QVector<NodeViewItem*>& items)
|
||||
{
|
||||
DetachItemsFromCursor();
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
foreach (NodeViewItem* i, items) {
|
||||
attached_items_.append({i, i->pos() - items.first()->pos()});
|
||||
}
|
||||
|
||||
setMouseTracking(true);
|
||||
|
||||
MoveAttachedNodesToCursor(mapFromGlobal(QCursor::pos()));
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::DetachItemsFromCursor()
|
||||
{
|
||||
attached_items_.clear();
|
||||
setMouseTracking(false);
|
||||
}
|
||||
|
||||
void NodeView::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
{
|
||||
scene_.SetFlowDirection(dir);
|
||||
}
|
||||
|
||||
void NodeView::MoveAttachedNodesToCursor(const QPoint& p)
|
||||
{
|
||||
QPointF item_pos = mapToScene(p);
|
||||
|
||||
foreach (const AttachedItem& i, attached_items_) {
|
||||
i.item->setPos(item_pos + i.original_pos);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::ConnectSelectionChangedSignal()
|
||||
{
|
||||
connect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot);
|
||||
}
|
||||
|
||||
void NodeView::DisconnectSelectionChangedSignal()
|
||||
{
|
||||
disconnect(&scene_, &QGraphicsScene::selectionChanged, this, &NodeView::SceneSelectionChangedSlot);
|
||||
}
|
||||
|
||||
void NodeView::UpdateBlockFilter()
|
||||
{
|
||||
// Hide all nodes
|
||||
foreach (NodeViewItem* item, scene_.item_map()) {
|
||||
item->setVisible(false);
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
QPointF last_bottom_right;
|
||||
|
||||
QVector<Node*> currently_visible;
|
||||
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
// Auto-position this node's dependencies
|
||||
scene_.ReorganizeFrom(b);
|
||||
|
||||
// Start calculating the bounding rect of this node's deps
|
||||
QPointF node_pos = b->GetPosition();
|
||||
QRectF anchor(node_pos, node_pos);
|
||||
|
||||
QVector<Node*> deps = b->GetDependencies();
|
||||
|
||||
foreach (Node* d, deps) {
|
||||
QPointF dep_pos = d->GetPosition();
|
||||
|
||||
anchor.setLeft(qMin(anchor.left(), dep_pos.x()));
|
||||
anchor.setRight(qMax(anchor.right(), dep_pos.x()));
|
||||
anchor.setTop(qMin(anchor.top(), dep_pos.y()));
|
||||
anchor.setBottom(qMax(anchor.bottom(), dep_pos.y()));
|
||||
|
||||
NodeViewItem* item = scene_.NodeToUIObject(d);
|
||||
if (item) {
|
||||
item->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Shift the bounding rect in relation to the other bounding rects
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
QPointF desired_anchor_pos = last_bottom_right + QPointF(0, 2);
|
||||
|
||||
QPointF necessary_movement = anchor.topRight() - desired_anchor_pos;
|
||||
|
||||
// Calculate the bottom right from which we'll anchor the next rect
|
||||
last_bottom_right = anchor.bottomRight() - necessary_movement;
|
||||
|
||||
b->SetPosition(b->GetPosition() - necessary_movement);
|
||||
foreach (Node* d, deps) {
|
||||
d->SetPosition(d->GetPosition() - necessary_movement);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we're done calculating the bounding rect, add this block...
|
||||
deps.append(b);
|
||||
|
||||
// ...then add its associations
|
||||
deps.append(temporary_association_map_[b]);
|
||||
for (auto i=association_map_.begin(); i!=association_map_.end(); i++) {
|
||||
if (i.value().contains(b)) {
|
||||
deps.append(i.key());
|
||||
}
|
||||
}
|
||||
|
||||
// And make sure all nodes are shown
|
||||
foreach (Node* n, deps) {
|
||||
NodeViewItem* item = scene_.NodeToUIObject(n);
|
||||
if (item) {
|
||||
item->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
// And lastly, add them all to our currently visible list
|
||||
currently_visible.append(deps);
|
||||
}
|
||||
|
||||
// Show only edges between those dependencies
|
||||
foreach (NodeViewEdge* edge, scene_.edge_map()) {
|
||||
edge->setVisible((currently_visible.contains(edge->edge()->input_node())
|
||||
&& currently_visible.contains(edge->edge()->output_node())));
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::AssociateNodeWithSelectedBlocks(Node *n)
|
||||
{
|
||||
association_map_.insert(n, selected_blocks_);
|
||||
connect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed, Qt::DirectConnection);
|
||||
}
|
||||
|
||||
void NodeView::DisassociateNode(Node *n, bool remove_from_map)
|
||||
{
|
||||
if (remove_from_map) {
|
||||
association_map_.remove(n);
|
||||
}
|
||||
|
||||
disconnect(n, &Node::destroyed, this, &NodeView::AssociatedNodeDestroyed);
|
||||
}
|
||||
|
||||
void NodeView::QueueSelectBlocksInternal()
|
||||
{
|
||||
select_blocks_internal_timer_.stop();
|
||||
select_blocks_internal_timer_.start();
|
||||
}
|
||||
|
||||
void NodeView::SelectBlocksInternal()
|
||||
{
|
||||
// Block scene signals while our selection is changing a lot
|
||||
DisconnectSelectionChangedSignal();
|
||||
|
||||
if (filter_mode_ == kFilterShowSelectedBlocks) {
|
||||
UpdateBlockFilter();
|
||||
}
|
||||
|
||||
QVector<Node*> nodes;
|
||||
nodes.reserve(selected_blocks_.size());
|
||||
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
nodes.append(b);
|
||||
nodes.append(b->GetDependencies());
|
||||
}
|
||||
|
||||
SelectWithDependencies(nodes);
|
||||
|
||||
// Stop blocking signals and send a change signal now that all of our processing is done
|
||||
ConnectSelectionChangedSignal();
|
||||
SceneSelectionChangedSlot();
|
||||
|
||||
if (!selected_blocks_.isEmpty()) {
|
||||
NodeViewItem* item = scene_.NodeToUIObject(selected_blocks_.first());
|
||||
if (item) {
|
||||
centerOn(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::ValidateFilter()
|
||||
{
|
||||
// Force auto-positioning
|
||||
switch (filter_mode_) {
|
||||
case kFilterShowAll:
|
||||
// NOTE: Assumes Sequence
|
||||
scene_.ReorganizeFrom(static_cast<Sequence*>(graph_)->viewer_output());
|
||||
break;
|
||||
case kFilterShowSelectedBlocks:
|
||||
UpdateBlockFilter();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::AssociatedNodeDestroyed()
|
||||
{
|
||||
DisassociateNode(static_cast<Node*>(sender()), true);
|
||||
}
|
||||
|
||||
void NodeView::GraphNodeRemoved(Node *node)
|
||||
{
|
||||
if (selected_blocks_.contains(static_cast<Block*>(node))) {
|
||||
DeselectBlocks({static_cast<Block*>(node)});
|
||||
}
|
||||
}
|
||||
|
||||
void NodeView::GraphEdgeAdded(NodeEdgePtr edge)
|
||||
{
|
||||
Node* input_node = edge->input()->parentNode();
|
||||
|
||||
if (input_node->OutputsTo(static_cast<Sequence*>(graph_)->viewer_output(), true)) {
|
||||
auto i = association_map_.begin();
|
||||
|
||||
while (i != association_map_.end()) {
|
||||
if (input_node->InputsFrom(i.key(), true)) {
|
||||
i = association_map_.erase(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scene_.AddEdge(edge);
|
||||
|
||||
ValidateFilter();
|
||||
}
|
||||
|
||||
void NodeView::GraphEdgeRemoved(NodeEdgePtr edge)
|
||||
{
|
||||
scene_.RemoveEdge(edge);
|
||||
|
||||
Node* output_node = edge->output_node();
|
||||
|
||||
// Check if this disconnected node still connects to a selected block, in which case do nothing
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
if (output_node->OutputsTo(b, true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QVector<Node*> disconnected_nodes;
|
||||
disconnected_nodes.append(output_node);
|
||||
disconnected_nodes.append(output_node->GetDependencies());
|
||||
|
||||
if (output_node->OutputsTo(static_cast<Sequence*>(graph_)->viewer_output(), true)) {
|
||||
// Check if this disconnected node still has a path to the viewer somewhere else
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
QVector<Node*>& temp_assocs = temporary_association_map_[b];
|
||||
|
||||
temp_assocs.append(disconnected_nodes);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we must associate these nodes
|
||||
foreach (Node* n, disconnected_nodes) {
|
||||
AssociateNodeWithSelectedBlocks(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+11
-136
@@ -22,172 +22,47 @@
|
||||
#define NODEVIEW_H
|
||||
|
||||
#include <QGraphicsView>
|
||||
#include <QTimer>
|
||||
|
||||
#include "node/graph.h"
|
||||
#include "node/nodecopypaste.h"
|
||||
#include "nodeviewscene.h"
|
||||
#include "widget/handmovableview/handmovableview.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief A widget for viewing and editing node graphs
|
||||
*
|
||||
* This widget takes a NodeGraph object and constructs a QGraphicsScene representing its data, viewing and allowing
|
||||
* the user to make modifications to it.
|
||||
*/
|
||||
class NodeView : public HandMovableView, public NodeCopyPasteService
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeView(QWidget* parent);
|
||||
|
||||
virtual ~NodeView() override;
|
||||
|
||||
/**
|
||||
* @brief Sets the graph to view
|
||||
*/
|
||||
void SetGraph(NodeGraph* graph);
|
||||
void SetGraph(NodeGraph* graph){}
|
||||
|
||||
/**
|
||||
* @brief Delete selected nodes from graph (user-friendly/undoable)
|
||||
*/
|
||||
void DeleteSelected();
|
||||
void DeleteSelected(){}
|
||||
|
||||
void SelectAll();
|
||||
void DeselectAll();
|
||||
void SelectAll(){}
|
||||
void DeselectAll(){}
|
||||
|
||||
void Select(const QVector<Node*>& nodes);
|
||||
void SelectWithDependencies(QVector<Node *> nodes);
|
||||
void Select(const QVector<Node*>& nodes){}
|
||||
void SelectWithDependencies(QVector<Node *> nodes){}
|
||||
|
||||
void CopySelected(bool cut);
|
||||
void Paste();
|
||||
void CopySelected(bool cut){}
|
||||
void Paste(){}
|
||||
|
||||
void Duplicate();
|
||||
void Duplicate(){}
|
||||
|
||||
void SelectBlocks(const QVector<Block*>& blocks);
|
||||
void SelectBlocks(const QVector<Block*>& blocks){}
|
||||
|
||||
void DeselectBlocks(const QVector<Block*>& blocks);
|
||||
|
||||
signals:
|
||||
void NodesSelected(const QVector<Node*>& nodes);
|
||||
|
||||
void NodesDeselected(const QVector<Node*>& nodes);
|
||||
|
||||
protected:
|
||||
virtual void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
|
||||
virtual void wheelEvent(QWheelEvent* event) override;
|
||||
void DeselectBlocks(const QVector<Block*>& blocks){}
|
||||
|
||||
private:
|
||||
void AttachNodesToCursor(const QVector<Node *> &nodes);
|
||||
|
||||
void AttachItemsToCursor(const QVector<NodeViewItem *> &items);
|
||||
|
||||
void DetachItemsFromCursor();
|
||||
|
||||
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
|
||||
|
||||
void MoveAttachedNodesToCursor(const QPoint &p);
|
||||
|
||||
void ConnectSelectionChangedSignal();
|
||||
void DisconnectSelectionChangedSignal();
|
||||
|
||||
void UpdateBlockFilter();
|
||||
|
||||
void AssociateNodeWithSelectedBlocks(Node* n);
|
||||
void DisassociateNode(Node* n, bool remove_from_map);
|
||||
|
||||
void QueueSelectBlocksInternal();
|
||||
|
||||
QTimer select_blocks_internal_timer_;
|
||||
|
||||
NodeGraph* graph_;
|
||||
|
||||
struct AttachedItem {
|
||||
NodeViewItem* item;
|
||||
QPointF original_pos;
|
||||
};
|
||||
|
||||
QList<AttachedItem> attached_items_;
|
||||
|
||||
NodeViewEdge* drop_edge_;
|
||||
NodeInput* drop_input_;
|
||||
|
||||
NodeViewScene scene_;
|
||||
|
||||
QVector<Node*> selected_nodes_;
|
||||
|
||||
QVector<Block*> selected_blocks_;
|
||||
|
||||
QHash<Node*, QVector<Block*> > association_map_;
|
||||
|
||||
QHash<Block*, QVector<Node*> > temporary_association_map_;
|
||||
|
||||
enum FilterMode {
|
||||
kFilterShowAll,
|
||||
kFilterShowSelectedBlocks
|
||||
};
|
||||
|
||||
FilterMode filter_mode_;
|
||||
|
||||
double scale_;
|
||||
|
||||
private slots:
|
||||
void ValidateFilter();
|
||||
|
||||
void AssociatedNodeDestroyed();
|
||||
|
||||
void GraphNodeRemoved(Node* node);
|
||||
|
||||
void GraphEdgeAdded(NodeEdgePtr edge);
|
||||
|
||||
void GraphEdgeRemoved(NodeEdgePtr edge);
|
||||
|
||||
/**
|
||||
* @brief Internal function triggered when any change is signalled from the QGraphicsScene
|
||||
*
|
||||
* Current primary function is to inform all NodeViewEdges to re-adjust in case any Nodes have moved
|
||||
*/
|
||||
void ItemsChanged();
|
||||
|
||||
/**
|
||||
* @brief Receiver for when the scene's selected items change
|
||||
*/
|
||||
void SceneSelectionChangedSlot();
|
||||
|
||||
/**
|
||||
* @brief Receiver for when the user right clicks (or otherwise requests a context menu)
|
||||
*/
|
||||
void ShowContextMenu(const QPoint &pos);
|
||||
|
||||
/**
|
||||
* @brief Receiver for when the user requests a new node from the add menu
|
||||
*/
|
||||
void CreateNodeSlot(QAction* action);
|
||||
|
||||
/**
|
||||
* @brief Receiver for setting the direction from the context menu
|
||||
*/
|
||||
void ContextMenuSetDirection(QAction* action);
|
||||
|
||||
/**
|
||||
* @brief Receiver for auto-position descendents menu action
|
||||
*/
|
||||
void AutoPositionDescendents();
|
||||
|
||||
/**
|
||||
* @brief Receiver for the user changing the filter
|
||||
*/
|
||||
void ContextMenuFilterChanged(QAction* action);
|
||||
|
||||
void SelectBlocksInternal();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/***
|
||||
|
||||
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 NODEVIEWCOMMON_H
|
||||
#define NODEVIEWCOMMON_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeViewCommon {
|
||||
public:
|
||||
enum FlowDirection {
|
||||
kTopToBottom,
|
||||
kBottomToTop,
|
||||
kLeftToRight,
|
||||
kRightToLeft
|
||||
};
|
||||
|
||||
static Qt::Orientation GetFlowOrientation(FlowDirection dir) {
|
||||
if (dir == kTopToBottom || dir == kBottomToTop) {
|
||||
return Qt::Vertical;
|
||||
} else {
|
||||
return Qt::Horizontal;
|
||||
}
|
||||
}
|
||||
|
||||
static bool DirectionsAreOpposing(FlowDirection a, FlowDirection b) {
|
||||
return ((a == NodeViewCommon::kLeftToRight && b == NodeViewCommon::kRightToLeft)
|
||||
|| (a == NodeViewCommon::kRightToLeft && b == NodeViewCommon::kLeftToRight)
|
||||
|| (a == NodeViewCommon::kTopToBottom && b == NodeViewCommon::kBottomToTop)
|
||||
|| (a == NodeViewCommon::kBottomToTop && b == NodeViewCommon::kTopToBottom));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVIEWCOMMON_H
|
||||
@@ -1,223 +0,0 @@
|
||||
/***
|
||||
|
||||
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 "nodeviewedge.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
|
||||
#include "common/bezier.h"
|
||||
#include "common/lerp.h"
|
||||
#include "nodeview.h"
|
||||
#include "nodeviewscene.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeViewEdge::NodeViewEdge(QGraphicsItem *parent) :
|
||||
QGraphicsPathItem(parent),
|
||||
edge_(nullptr),
|
||||
connected_(false),
|
||||
highlighted_(false),
|
||||
flow_dir_(NodeViewCommon::kLeftToRight),
|
||||
curved_(true)
|
||||
{
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
|
||||
// Ensures this UI object is drawn behind other objects
|
||||
setZValue(-1);
|
||||
|
||||
// Use font metrics to set edge width for basic high DPI support
|
||||
edge_width_ = QFontMetrics(QFont()).height() / 12;
|
||||
arrow_size_ = QFontMetrics(QFont()).height() / 2;
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetEdge(NodeEdgePtr edge)
|
||||
{
|
||||
SetConnected(true);
|
||||
|
||||
// Set the new edge pointer
|
||||
edge_ = edge;
|
||||
|
||||
// Re-adjust the line positioning for this new edge
|
||||
Adjust();
|
||||
}
|
||||
|
||||
NodeEdgePtr NodeViewEdge::edge()
|
||||
{
|
||||
return edge_;
|
||||
}
|
||||
|
||||
void NodeViewEdge::Adjust()
|
||||
{
|
||||
if (!edge_ || !scene()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the UI objects of both nodes that this edge connects
|
||||
NodeViewItem* output = static_cast<NodeViewScene*>(scene())->NodeToUIObject(edge_->output()->parentNode());
|
||||
NodeViewItem* input = static_cast<NodeViewScene*>(scene())->NodeToUIObject(edge_->input()->parentNode());
|
||||
|
||||
if (!output || !input) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw a line between the two
|
||||
SetPoints(output->GetParamPoint(edge_->output(), output->pos()),
|
||||
input->GetParamPoint(edge_->input(), output->pos()),
|
||||
input->IsExpanded());
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetConnected(bool c)
|
||||
{
|
||||
connected_ = c;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetHighlighted(bool e)
|
||||
{
|
||||
highlighted_ = e;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetPoints(const QPointF &start, const QPointF &end, bool input_is_expanded)
|
||||
{
|
||||
QPainterPath path;
|
||||
path.moveTo(start);
|
||||
|
||||
double angle = qAtan2(end.y() - start.y(), end.x() - start.x());
|
||||
|
||||
if (curved_) {
|
||||
|
||||
double half_x = lerp(start.x(), end.x(), 0.5);
|
||||
double half_y = lerp(start.y(), end.y(), 0.5);
|
||||
|
||||
QPointF cp1, cp2;
|
||||
|
||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
|
||||
cp1 = QPointF(half_x, start.y());
|
||||
} else {
|
||||
cp1 = QPointF(start.x(), half_y);
|
||||
}
|
||||
|
||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal || input_is_expanded) {
|
||||
cp2 = QPointF(half_x, end.y());
|
||||
} else {
|
||||
cp2 = QPointF(end.x(), half_y);
|
||||
}
|
||||
|
||||
path.cubicTo(cp1, cp2, end);
|
||||
|
||||
if (!qFuzzyCompare(start.x(), end.x())) {
|
||||
double continue_x = end.x() - qCos(angle)*arrow_size_;
|
||||
|
||||
double x1, x2, x3, x4, y1, y2, y3, y4;
|
||||
if (start.x() < end.x()) {
|
||||
x1 = start.x();
|
||||
x2 = cp1.x();
|
||||
x3 = cp2.x();
|
||||
x4 = end.x();
|
||||
y1 = start.y();
|
||||
y2 = cp1.y();
|
||||
y3 = cp2.y();
|
||||
y4 = end.y();
|
||||
} else {
|
||||
x1 = end.x();
|
||||
x2 = cp2.x();
|
||||
x3 = cp1.x();
|
||||
x4 = start.x();
|
||||
y1 = end.y();
|
||||
y2 = cp2.y();
|
||||
y3 = cp1.y();
|
||||
y4 = start.y();
|
||||
}
|
||||
|
||||
double t = Bezier::CubicXtoT(continue_x, x1, x2, x3, x4);
|
||||
double y = Bezier::CubicTtoY(y1, y2, y3, y4, t);
|
||||
|
||||
angle = qAtan2(end.y() - y, end.x() - continue_x);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
path.lineTo(end);
|
||||
|
||||
}
|
||||
|
||||
setPath(path);
|
||||
|
||||
const double arrow_angle = 150.0 * 3.141592 / 180.0;
|
||||
QVector<QPointF> arrow_points(4);
|
||||
arrow_points[0] = end;
|
||||
arrow_points[1] = end + QPointF(qCos(angle + arrow_angle) * arrow_size_, qSin(angle + arrow_angle) * arrow_size_);
|
||||
arrow_points[2] = end + QPointF(qCos(angle - arrow_angle) * arrow_size_, qSin(angle - arrow_angle) * arrow_size_);
|
||||
arrow_points[3] = end;
|
||||
|
||||
arrow_ = QPolygonF(arrow_points);
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
{
|
||||
flow_dir_ = dir;
|
||||
|
||||
Adjust();
|
||||
}
|
||||
|
||||
void NodeViewEdge::SetCurved(bool e)
|
||||
{
|
||||
curved_ = e;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void NodeViewEdge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
|
||||
{
|
||||
QPalette::ColorGroup group;
|
||||
QPalette::ColorRole role;
|
||||
|
||||
if (connected_) {
|
||||
group = QPalette::Active;
|
||||
} else {
|
||||
group = QPalette::Disabled;
|
||||
}
|
||||
|
||||
if (highlighted_ != bool(option->state & QStyle::State_Selected)) {
|
||||
role = QPalette::Highlight;
|
||||
} else {
|
||||
role = QPalette::Text;
|
||||
}
|
||||
|
||||
// Draw main path
|
||||
QColor edge_color = qApp->palette().color(group, role);
|
||||
|
||||
painter->setPen(QPen(edge_color, edge_width_));
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawPath(path());
|
||||
|
||||
// Draw arrow
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(edge_color);
|
||||
painter->drawPolygon(arrow_);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/***
|
||||
|
||||
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 NODEEDGEITEM_H
|
||||
#define NODEEDGEITEM_H
|
||||
|
||||
#include <QGraphicsPathItem>
|
||||
#include <QPalette>
|
||||
|
||||
#include "node/edge.h"
|
||||
#include "nodeviewcommon.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief A graphical representation of a NodeEdge to be used in NodeView
|
||||
*
|
||||
* A fairly simple line widget use to visualize a connection between two node parameters (a NodeEdge).
|
||||
*/
|
||||
class NodeViewEdge : public QGraphicsPathItem
|
||||
{
|
||||
public:
|
||||
NodeViewEdge(QGraphicsItem* parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Set the edge that this item corresponds to
|
||||
*
|
||||
* This can be changed at any time (but under most circumstances won't be). Calling this will automatically call
|
||||
* Adjust() to move this item into the correct position.
|
||||
*/
|
||||
void SetEdge(NodeEdgePtr edge);
|
||||
NodeEdgePtr edge();
|
||||
|
||||
/**
|
||||
* @brief Moves/updates this line to visually connect between the two corresponding NodeViewItems
|
||||
*
|
||||
* Using the attached edge (see SetEdge()), this function retrieves the NodeViewItems representing the two nodes
|
||||
* that this edge connects. It uses their positions to determine where the line should visually connect and sets
|
||||
* it accordingly.
|
||||
*
|
||||
* This should be set any time the NodeEdge changes (see SetEdge()), and any time the nodes move in the NodeGraph
|
||||
* (see NodeView::ItemsChanged()). This will keep the nodes visually connected at all times.
|
||||
*/
|
||||
void Adjust();
|
||||
|
||||
/**
|
||||
* @brief Set the connected state of this line
|
||||
*
|
||||
* When the edge is not connected, it visually depicts this by coloring the line grey. When an edge is connected or
|
||||
* a potential connection is valid, the line is colored white. This function sets whether the line should be grey
|
||||
* (false) or white (true).
|
||||
*
|
||||
* Using SetEdge() automatically sets this to true. Under most circumstances this should be left alone, and only
|
||||
* be set when an edge is being created/dragged.
|
||||
*/
|
||||
void SetConnected(bool c);
|
||||
|
||||
/**
|
||||
* @brief Set highlighted state
|
||||
*
|
||||
* Changes color of edge.
|
||||
*/
|
||||
void SetHighlighted(bool e);
|
||||
|
||||
/**
|
||||
* @brief Set points to create curve from
|
||||
*/
|
||||
void SetPoints(const QPointF& start, const QPointF& end, bool input_is_expanded);
|
||||
|
||||
/**
|
||||
* @brief Sets the direction nodes are flowing
|
||||
*/
|
||||
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
|
||||
|
||||
/**
|
||||
* @brief Set whether edges should be drawn as curved or as straight lines
|
||||
*/
|
||||
void SetCurved(bool e);
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
private:
|
||||
NodeEdgePtr edge_;
|
||||
|
||||
int edge_width_;
|
||||
|
||||
bool connected_;
|
||||
|
||||
bool highlighted_;
|
||||
|
||||
NodeViewCommon::FlowDirection flow_dir_;
|
||||
|
||||
bool curved_;
|
||||
|
||||
QPolygonF arrow_;
|
||||
|
||||
int arrow_size_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEEDGEITEM_H
|
||||
@@ -1,702 +0,0 @@
|
||||
/***
|
||||
|
||||
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 "nodeviewitem.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsSceneMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
|
||||
#include "common/flipmodifiers.h"
|
||||
#include "common/qtutils.h"
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "nodeview.h"
|
||||
#include "nodeviewscene.h"
|
||||
#include "nodeviewundo.h"
|
||||
#include "ui/icons/icons.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeViewItem::NodeViewItem(QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
node_(nullptr),
|
||||
dragging_edge_(nullptr),
|
||||
cached_drop_item_(nullptr),
|
||||
cached_drop_item_expanded_(false),
|
||||
expanded_(false),
|
||||
hide_titlebar_(false),
|
||||
standard_click_(false),
|
||||
highlighted_index_(-1),
|
||||
node_edge_change_command_(nullptr),
|
||||
flow_dir_(NodeViewCommon::kLeftToRight)
|
||||
{
|
||||
// Set flags for this widget
|
||||
setFlag(QGraphicsItem::ItemIsMovable);
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
|
||||
|
||||
//
|
||||
// We use font metrics to set all the UI measurements for DPI-awareness
|
||||
//
|
||||
|
||||
// Set border width
|
||||
node_border_width_ = DefaultItemBorder();
|
||||
|
||||
int widget_width = DefaultItemWidth();
|
||||
int widget_height = DefaultItemHeight();
|
||||
|
||||
title_bar_rect_ = QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height);
|
||||
setRect(title_bar_rect_);
|
||||
}
|
||||
|
||||
QPointF NodeViewItem::GetNodePosition() const
|
||||
{
|
||||
QPointF node_pos;
|
||||
|
||||
qreal adjusted_x = pos().x() / DefaultItemHorizontalPadding();
|
||||
qreal adjusted_y = pos().y() / DefaultItemVerticalPadding();
|
||||
|
||||
switch (flow_dir_) {
|
||||
case NodeViewCommon::kLeftToRight:
|
||||
node_pos.setX(adjusted_x);
|
||||
node_pos.setY(adjusted_y);
|
||||
break;
|
||||
case NodeViewCommon::kRightToLeft:
|
||||
node_pos.setX(-adjusted_x);
|
||||
node_pos.setY(adjusted_y);
|
||||
break;
|
||||
case NodeViewCommon::kTopToBottom:
|
||||
node_pos.setX(adjusted_y);
|
||||
node_pos.setY(adjusted_x);
|
||||
break;
|
||||
case NodeViewCommon::kBottomToTop:
|
||||
node_pos.setX(-adjusted_y);
|
||||
node_pos.setY(adjusted_x);
|
||||
break;
|
||||
}
|
||||
|
||||
return node_pos;
|
||||
}
|
||||
|
||||
void NodeViewItem::SetNodePosition(const QPointF &pos)
|
||||
{
|
||||
switch (flow_dir_) {
|
||||
case NodeViewCommon::kLeftToRight:
|
||||
setPos(pos.x() * DefaultItemHorizontalPadding(),
|
||||
pos.y() * DefaultItemVerticalPadding());
|
||||
break;
|
||||
case NodeViewCommon::kRightToLeft:
|
||||
setPos(-pos.x() * DefaultItemHorizontalPadding(),
|
||||
pos.y() * DefaultItemVerticalPadding());
|
||||
break;
|
||||
case NodeViewCommon::kTopToBottom:
|
||||
setPos(pos.y() * DefaultItemHorizontalPadding(),
|
||||
pos.x() * DefaultItemVerticalPadding());
|
||||
break;
|
||||
case NodeViewCommon::kBottomToTop:
|
||||
setPos(pos.y() * DefaultItemHorizontalPadding(),
|
||||
-pos.x() * DefaultItemVerticalPadding());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int NodeViewItem::DefaultTextPadding()
|
||||
{
|
||||
return QFontMetrics(QFont()).height() / 4;
|
||||
}
|
||||
|
||||
int NodeViewItem::DefaultItemHeight()
|
||||
{
|
||||
return QFontMetrics(QFont()).height() + DefaultTextPadding() * 2;
|
||||
}
|
||||
|
||||
int NodeViewItem::DefaultItemWidth()
|
||||
{
|
||||
return QtUtils::QFontMetricsWidth(QFontMetrics(QFont()), "HHHHHHHHHHHH");;
|
||||
}
|
||||
|
||||
int NodeViewItem::DefaultItemBorder()
|
||||
{
|
||||
return QFontMetrics(QFont()).height() / 12;
|
||||
}
|
||||
|
||||
qreal NodeViewItem::DefaultItemHorizontalPadding() const
|
||||
{
|
||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
|
||||
return DefaultItemWidth() * 1.5;
|
||||
} else {
|
||||
return DefaultItemWidth() * 1.25;
|
||||
}
|
||||
}
|
||||
|
||||
qreal NodeViewItem::DefaultItemVerticalPadding() const
|
||||
{
|
||||
if (NodeViewCommon::GetFlowOrientation(flow_dir_) == Qt::Horizontal) {
|
||||
return DefaultItemHeight() * 1.5;
|
||||
} else {
|
||||
return DefaultItemHeight() * 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::SetNode(Node *n)
|
||||
{
|
||||
node_ = n;
|
||||
|
||||
node_inputs_.clear();
|
||||
|
||||
if (node_) {
|
||||
node_->Retranslate();
|
||||
|
||||
foreach (NodeParam* p, node_->parameters()) {
|
||||
if (p->type() == NodeParam::kInput) {
|
||||
NodeInput* input = static_cast<NodeInput*>(p);
|
||||
|
||||
if (input->is_connectable()) {
|
||||
node_inputs_.append(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetNodePosition(node_->GetPosition());
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
Node *NodeViewItem::GetNode() const
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
bool NodeViewItem::IsExpanded() const
|
||||
{
|
||||
return expanded_;
|
||||
}
|
||||
|
||||
void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
|
||||
{
|
||||
if (node_inputs_.isEmpty()
|
||||
|| (expanded_ == e && hide_titlebar_ == hide_titlebar)) {
|
||||
return;
|
||||
}
|
||||
|
||||
expanded_ = e;
|
||||
hide_titlebar_ = hide_titlebar;
|
||||
|
||||
if (expanded_ && !node_inputs_.isEmpty()) {
|
||||
// Create new rect
|
||||
QRectF new_rect = title_bar_rect_;
|
||||
|
||||
if (hide_titlebar_) {
|
||||
new_rect.setHeight(new_rect.height() * node_inputs_.size());
|
||||
} else {
|
||||
new_rect.setHeight(new_rect.height() * (node_inputs_.size() + 1));
|
||||
}
|
||||
|
||||
setRect(new_rect);
|
||||
} else {
|
||||
setRect(title_bar_rect_);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void NodeViewItem::ToggleExpanded()
|
||||
{
|
||||
SetExpanded(!IsExpanded());
|
||||
}
|
||||
|
||||
void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
|
||||
{
|
||||
// HACK for getting the main QWidget palette color (the `widget`'s palette uses the NodeView color instead which we
|
||||
// don't want here)
|
||||
QPalette app_pal = Core::instance()->main_window()->palette();
|
||||
|
||||
// Draw background rect if expanded
|
||||
if (IsExpanded()) {
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(app_pal.color(QPalette::Window));
|
||||
|
||||
painter->drawRect(rect());
|
||||
|
||||
painter->setPen(app_pal.color(QPalette::Text));
|
||||
|
||||
for (int i=0;i<node_inputs_.size();i++) {
|
||||
QRectF input_rect = GetInputRect(i);
|
||||
|
||||
if (highlighted_index_ == i) {
|
||||
QColor highlight_col = app_pal.color(QPalette::Text);
|
||||
highlight_col.setAlpha(64);
|
||||
painter->fillRect(input_rect, highlight_col);
|
||||
}
|
||||
|
||||
painter->drawText(input_rect, Qt::AlignCenter, node_inputs_.at(i)->name());
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the titlebar
|
||||
if (!hide_titlebar_ && node_) {
|
||||
|
||||
Color node_color = Config::Current()[QStringLiteral("NodeCatColor%1")
|
||||
.arg(node_->Category().first())].value<Color>();
|
||||
|
||||
QLinearGradient grad;
|
||||
|
||||
grad.setStart(0, rect().top());
|
||||
grad.setFinalStop(0, rect().bottom());
|
||||
|
||||
QColor node_color_as_qcolor = node_color.toQColor();
|
||||
|
||||
{
|
||||
// Generate lighter color for gradient
|
||||
qreal hue, sat, lightness;
|
||||
node_color_as_qcolor.getHslF(&hue, &sat, &lightness);
|
||||
lightness = qMin(1.0, lightness + 0.2);
|
||||
grad.setColorAt(0.0, QColor::fromHslF(hue, sat, lightness));
|
||||
}
|
||||
|
||||
grad.setColorAt(1.0, node_color_as_qcolor);
|
||||
|
||||
painter->setPen(Qt::black);
|
||||
painter->setBrush(grad);
|
||||
|
||||
painter->drawRect(title_bar_rect_);
|
||||
|
||||
painter->setPen(app_pal.color(QPalette::Text));
|
||||
|
||||
QString node_label;
|
||||
|
||||
if (!node_->GetLabel().isEmpty()) {
|
||||
// Use label directly if node has one
|
||||
node_label = node_->GetLabel();
|
||||
} else if (node_->IsTrack()) {
|
||||
// If node is a track, use a special track name to help users identify better
|
||||
// Exception for tracks
|
||||
TrackOutput* track = static_cast<TrackOutput*>(node_);
|
||||
node_label = TrackOutput::GetDefaultTrackName(track->track_type(), track->Index());
|
||||
} else {
|
||||
// Otherwise, just use the node's short name
|
||||
node_label = node_->ShortName();
|
||||
}
|
||||
|
||||
QFont f;
|
||||
QFontMetrics fm(f);
|
||||
|
||||
// Draw right or down arrow based on expanded state
|
||||
int icon_size = fm.height() / 2;
|
||||
int icon_padding = title_bar_rect_.height() / 2 - icon_size / 2;
|
||||
int icon_full_size = icon_size + icon_padding * 2;
|
||||
const QIcon& expand_icon = IsExpanded() ? icon::TriDown : icon::TriRight;
|
||||
expand_icon.paint(painter, QRect(title_bar_rect_.x() + icon_padding,
|
||||
title_bar_rect_.y() + icon_padding,
|
||||
icon_size,
|
||||
icon_size));
|
||||
|
||||
// Calculate how much space we have for text
|
||||
int item_width = title_bar_rect_.width();
|
||||
int max_text_width = item_width - DefaultTextPadding() * 2 - icon_full_size;
|
||||
int label_width = QtUtils::QFontMetricsWidth(fm, node_label);
|
||||
|
||||
// Concatenate text if necessary (adds a "..." to the end and removes characters until the
|
||||
// string fits in the bounds)
|
||||
if (label_width > max_text_width) {
|
||||
QString concatenated;
|
||||
|
||||
do {
|
||||
node_label.chop(1);
|
||||
concatenated = QCoreApplication::translate("NodeViewItem", "%1...").arg(node_label);
|
||||
} while ((label_width = QtUtils::QFontMetricsWidth(fm, concatenated)) > max_text_width);
|
||||
|
||||
node_label = concatenated;
|
||||
}
|
||||
|
||||
// Determine the text color (automatically calculate from node background color)
|
||||
if (node_color.GetRoughLuminance() > 0.5) {
|
||||
painter->setPen(Qt::black);
|
||||
} else {
|
||||
painter->setPen(Qt::white);
|
||||
}
|
||||
|
||||
// Determine X position (favors horizontal centering unless it'll overrun the arrow)
|
||||
QRectF text_rect = title_bar_rect_;
|
||||
Qt::Alignment text_align = Qt::AlignCenter;
|
||||
int likely_x = item_width / 2 - label_width / 2;
|
||||
if (likely_x < icon_full_size) {
|
||||
text_rect.adjust(icon_full_size, 0, 0, 0);
|
||||
text_align = Qt::AlignLeft | Qt::AlignVCenter;
|
||||
}
|
||||
|
||||
// Draw the text in a rect (the rect is sized around text already in the constructor)
|
||||
painter->drawText(text_rect,
|
||||
text_align,
|
||||
node_label);
|
||||
}
|
||||
|
||||
// Draw final border
|
||||
QPen border_pen;
|
||||
border_pen.setWidth(node_border_width_);
|
||||
|
||||
if (option->state & QStyle::State_Selected) {
|
||||
border_pen.setColor(app_pal.color(QPalette::Highlight));
|
||||
} else {
|
||||
border_pen.setColor(Qt::black);
|
||||
}
|
||||
|
||||
painter->setPen(border_pen);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
painter->drawRect(rect());
|
||||
}
|
||||
|
||||
void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
// We override standard mouse behavior in some cases. In these cases, we don't want the standard "move" and "release"
|
||||
// events to trigger if we haven't already triggered the "press" event. We use this variable to determine whether
|
||||
// base class behavior is valid here.
|
||||
standard_click_ = false;
|
||||
|
||||
// If CTRL is held, we start a new edge
|
||||
if (node_ && event->modifiers() & Qt::ControlModifier) {
|
||||
|
||||
NodeParam* param = node_->output();
|
||||
|
||||
// Create draggable object
|
||||
dragging_edge_ = new NodeViewEdge();
|
||||
dragging_edge_->SetFlowDirection(flow_dir_);
|
||||
|
||||
// Set up a QUndoCommand to make this action undoable
|
||||
node_edge_change_command_ = new QUndoCommand();
|
||||
|
||||
if (param->type() == NodeParam::kOutput || param->edges().isEmpty()) {
|
||||
// For an output param (or an input param with no connections), we default to creating a new edge
|
||||
drag_source_ = this;
|
||||
drag_src_param_ = param;
|
||||
|
||||
// Set the starting position to the current param's connector
|
||||
dragging_edge_start_ = GetParamPoint(param, QPointF());
|
||||
|
||||
} else if (param->type() == NodeParam::kInput) {
|
||||
// For an input param, we default to moving an existing edge
|
||||
// (here we use the last one, which will usually also be the first)
|
||||
NodeEdgePtr edge = param->edges().last();
|
||||
|
||||
// The starting position will be the OPPOSING parameter's rectangle
|
||||
|
||||
// Get the opposing parameter
|
||||
drag_src_param_ = edge->output();
|
||||
|
||||
// Get its Node UI object
|
||||
drag_source_ = static_cast<NodeViewScene*>(scene())->NodeToUIObject(drag_src_param_->parentNode());
|
||||
|
||||
// Get the opposing parameter's rect center using the line's current coordinates
|
||||
// (we use the current coordinates because a complex formula is used for the line's coords if
|
||||
// the opposing node is collapsed, therefore it's easier to just retrieve it from line itself)
|
||||
NodeViewEdge* existing_edge_ui = static_cast<NodeViewScene*>(scene())->EdgeToUIObject(edge);
|
||||
QPainterPath existing_edge_line = existing_edge_ui->path();
|
||||
QPointF edge_start = existing_edge_line.pointAtPercent(0);
|
||||
QPointF edge_end = existing_edge_line.pointAtPercent(1);
|
||||
if (existing_edge_ui->contains(edge_start)) {
|
||||
dragging_edge_start_ = edge_start;
|
||||
} else {
|
||||
dragging_edge_start_ = edge_end;
|
||||
}
|
||||
|
||||
|
||||
// Remove old edge
|
||||
NodeEdgeRemoveCommand* remove_command = new NodeEdgeRemoveCommand(edge->output(),
|
||||
edge->input(),
|
||||
node_edge_change_command_);
|
||||
remove_command->redo();
|
||||
|
||||
}
|
||||
|
||||
// Add it to the scene
|
||||
scene()->addItem(dragging_edge_);
|
||||
|
||||
// Trigger initial line setting
|
||||
mouseMoveEvent(event);
|
||||
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
// We aren't using any override behaviors, switch back to standard click behavior
|
||||
standard_click_ = true;
|
||||
|
||||
event->setModifiers(FlipControlAndShiftModifiers(event->modifiers()));
|
||||
|
||||
QGraphicsRectItem::mousePressEvent(event);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
// Check if an edge drag was initiated
|
||||
if (dragging_edge_ != nullptr) {
|
||||
QPointF end_point = event->scenePos();
|
||||
drag_dest_param_ = nullptr;
|
||||
|
||||
if (!cached_drop_item_
|
||||
|| !cached_drop_item_->contains(cached_drop_item_->mapFromScene(event->scenePos()))) {
|
||||
|
||||
// Cursor has left last entered item, we need to find a new one
|
||||
if (cached_drop_item_) {
|
||||
|
||||
// If we expanded an item below but are no longer dragging over it, re-collapse it
|
||||
if (cached_drop_item_expanded_) {
|
||||
cached_drop_item_->SetExpanded(false);
|
||||
}
|
||||
|
||||
cached_drop_item_->SetHighlightedIndex(-1);
|
||||
|
||||
cached_drop_item_->setZValue(0);
|
||||
cached_drop_item_ = nullptr;
|
||||
}
|
||||
|
||||
NodeViewItem* cursor_item = dynamic_cast<NodeViewItem*>(scene()->itemAt(event->scenePos(), sceneTransform()));
|
||||
|
||||
if (cursor_item
|
||||
&& cursor_item != drag_source_
|
||||
&& !cursor_item->node_inputs_.isEmpty()) {
|
||||
cached_drop_item_ = cursor_item;
|
||||
|
||||
// If the item we're dragging over is collapsed, expand it
|
||||
cached_drop_item_expanded_ = !cached_drop_item_->IsExpanded();
|
||||
|
||||
if (cached_drop_item_expanded_) {
|
||||
cached_drop_item_->SetExpanded(true, true);
|
||||
}
|
||||
|
||||
cached_drop_item_->setZValue(1);
|
||||
} else {
|
||||
cached_drop_item_ = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (cached_drop_item_) {
|
||||
|
||||
int highlight_their_index = -1;
|
||||
|
||||
// See if the mouse is currently inside a connector rect
|
||||
for (int i=0;i<cached_drop_item_->node_inputs_.size();i++) {
|
||||
|
||||
// Get the parameter we're dragging into
|
||||
NodeParam* comp_param = cached_drop_item_->node_inputs_.at(i);
|
||||
|
||||
// See if cursor is inside its UI
|
||||
if (cached_drop_item_->GetInputRect(i).contains(cached_drop_item_->mapFromScene(event->scenePos()))) { // See if we're dragging inside the hitbox
|
||||
|
||||
// Attempt to prevent circular dependency - ensure the receiving node doesn't output to the outputting node
|
||||
if (!cached_drop_item_->GetNode()->OutputsTo(node_, true)) {
|
||||
drag_dest_param_ = comp_param;
|
||||
highlight_their_index = i;
|
||||
|
||||
QPointF end_point_local = cached_drop_item_->GetInputPoint(i, pos());
|
||||
end_point = cached_drop_item_->mapToScene(end_point_local);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cached_drop_item_->SetHighlightedIndex(highlight_their_index);
|
||||
}
|
||||
|
||||
dragging_edge_->SetConnected(drag_dest_param_);
|
||||
|
||||
dragging_edge_->SetPoints(dragging_edge_start_,
|
||||
end_point,
|
||||
cached_drop_item_ ? cached_drop_item_->IsExpanded() : false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (standard_click_) {
|
||||
event->setModifiers(FlipControlAndShiftModifiers(event->modifiers()));
|
||||
|
||||
QGraphicsRectItem::mouseMoveEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
// Check if an edge drag was initiated
|
||||
if (dragging_edge_ != nullptr) {
|
||||
|
||||
// Remove the drag object
|
||||
scene()->removeItem(dragging_edge_);
|
||||
|
||||
// If we expanded an item in the drag, re-collapse it now
|
||||
if (cached_drop_item_) {
|
||||
if (cached_drop_item_expanded_) {
|
||||
cached_drop_item_->SetExpanded(false);
|
||||
}
|
||||
|
||||
cached_drop_item_->SetHighlightedIndex(-1);
|
||||
cached_drop_item_->setZValue(0);
|
||||
|
||||
cached_drop_item_ = nullptr;
|
||||
}
|
||||
|
||||
if (drag_dest_param_ != nullptr) {
|
||||
// We dragged to somewhere, so we'll make a new connection
|
||||
|
||||
NodeEdgePtr new_edge;
|
||||
|
||||
NodeOutput* output;
|
||||
NodeInput* input;
|
||||
|
||||
// Connecting will automatically add an edge UI object through the signal/slot system
|
||||
if (drag_dest_param_->type() == NodeParam::kOutput) {
|
||||
output = static_cast<NodeOutput*>(drag_dest_param_);
|
||||
input = static_cast<NodeInput*>(drag_src_param_);
|
||||
} else {
|
||||
output = static_cast<NodeOutput*>(drag_src_param_);
|
||||
input = static_cast<NodeInput*>(drag_dest_param_);
|
||||
}
|
||||
|
||||
// Use a command to make Node connecting undoable
|
||||
new NodeEdgeAddCommand(output,
|
||||
input,
|
||||
node_edge_change_command_);
|
||||
}
|
||||
|
||||
dragging_edge_ = nullptr;
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(node_edge_change_command_);
|
||||
node_edge_change_command_ = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (standard_click_) {
|
||||
event->setModifiers(FlipControlAndShiftModifiers(event->modifiers()));
|
||||
|
||||
QGraphicsRectItem::mouseReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
|
||||
{
|
||||
QGraphicsRectItem::mouseDoubleClickEvent(event);
|
||||
|
||||
if (!(event->modifiers() & Qt::ControlModifier)) {
|
||||
SetExpanded(!IsExpanded());
|
||||
}
|
||||
}
|
||||
|
||||
QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
if (change == ItemPositionHasChanged && node_) {
|
||||
node_->blockSignals(true);
|
||||
node_->SetPosition(GetNodePosition());
|
||||
node_->blockSignals(false);
|
||||
}
|
||||
|
||||
return QGraphicsItem::itemChange(change, value);
|
||||
}
|
||||
|
||||
void NodeViewItem::SetHighlightedIndex(int index)
|
||||
{
|
||||
if (highlighted_index_ == index) {
|
||||
return;
|
||||
}
|
||||
|
||||
highlighted_index_ = index;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
QRectF NodeViewItem::GetInputRect(int index) const
|
||||
{
|
||||
QRectF r = title_bar_rect_;
|
||||
|
||||
if (!hide_titlebar_) {
|
||||
index++;
|
||||
}
|
||||
|
||||
if (IsExpanded()) {
|
||||
r.translate(0, r.height() * index);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
QPointF NodeViewItem::GetParamPoint(NodeParam *param, const QPointF& source_pos) const
|
||||
{
|
||||
if (param->type() == NodeParam::kOutput) {
|
||||
|
||||
switch (flow_dir_) {
|
||||
case NodeViewCommon::kLeftToRight:
|
||||
default:
|
||||
return pos() + QPointF(rect().right(), rect().center().y());
|
||||
case NodeViewCommon::kRightToLeft:
|
||||
return pos() + QPointF(rect().left(), rect().center().y());
|
||||
case NodeViewCommon::kTopToBottom:
|
||||
return pos() + QPointF(rect().center().x(), rect().bottom());
|
||||
case NodeViewCommon::kBottomToTop:
|
||||
return pos() + QPointF(rect().center().x(), rect().top());
|
||||
}
|
||||
|
||||
} else {
|
||||
NodeInput* input = static_cast<NodeInput*>(param);
|
||||
|
||||
// Resolve NodeInputArray elements
|
||||
while (input->parentNode() != input->parent()) {
|
||||
input = static_cast<NodeInput*>(input->parent());
|
||||
}
|
||||
|
||||
return pos() + GetInputPoint(node_inputs_.indexOf(input), source_pos);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewItem::SetFlowDirection(NodeViewCommon::FlowDirection dir)
|
||||
{
|
||||
flow_dir_ = dir;
|
||||
}
|
||||
|
||||
QPointF NodeViewItem::GetInputPoint(int index, const QPointF& source_pos) const
|
||||
{
|
||||
QRectF input_rect = GetInputRect(index);
|
||||
|
||||
Qt::Orientation flow_orientation = NodeViewCommon::GetFlowOrientation(flow_dir_);
|
||||
|
||||
if (flow_orientation == Qt::Horizontal || IsExpanded()) {
|
||||
if (flow_dir_ == NodeViewCommon::kLeftToRight
|
||||
|| (flow_orientation == Qt::Vertical && source_pos.x() < pos().x())) {
|
||||
return QPointF(input_rect.left(), input_rect.center().y());
|
||||
} else {
|
||||
return QPointF(input_rect.right(), input_rect.center().y());
|
||||
}
|
||||
} else {
|
||||
if (flow_dir_ == NodeViewCommon::kTopToBottom) {
|
||||
return QPointF(input_rect.center().x(), input_rect.top());
|
||||
} else {
|
||||
return QPointF(input_rect.center().x(), input_rect.bottom());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/***
|
||||
|
||||
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 NODEVIEWITEM_H
|
||||
#define NODEVIEWITEM_H
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QLinearGradient>
|
||||
#include <QUndoCommand>
|
||||
#include <QWidget>
|
||||
|
||||
#include "node/node.h"
|
||||
#include "nodeviewcommon.h"
|
||||
#include "nodeviewedge.h"
|
||||
#include "nodeviewitemwidgetproxy.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief A visual widget representation of a Node object to be used in a NodeView
|
||||
*
|
||||
* This widget can be collapsed or expanded to show/hide the node's various parameters.
|
||||
*
|
||||
* To retrieve the NodeViewItem for a certain Node, use NodeView::NodeToUIObject().
|
||||
*/
|
||||
class NodeViewItem : public QGraphicsRectItem
|
||||
{
|
||||
public:
|
||||
NodeViewItem(QGraphicsItem* parent = nullptr);
|
||||
|
||||
QPointF GetNodePosition() const;
|
||||
void SetNodePosition(const QPointF& pos);
|
||||
|
||||
/**
|
||||
* @brief Set the Node to correspond to this widget
|
||||
*/
|
||||
void SetNode(Node* n);
|
||||
|
||||
/**
|
||||
* @brief Get currently attached node
|
||||
*/
|
||||
Node* GetNode() const;
|
||||
|
||||
/**
|
||||
* @brief Get expanded state
|
||||
*/
|
||||
bool IsExpanded() const;
|
||||
|
||||
/**
|
||||
* @brief Set expanded state
|
||||
*/
|
||||
void SetExpanded(bool e, bool hide_titlebar = false);
|
||||
void ToggleExpanded();
|
||||
|
||||
/**
|
||||
* @brief Returns GLOBAL point that edges should connect to for any NodeParam member of this object
|
||||
*/
|
||||
QPointF GetParamPoint(NodeParam* param, const QPointF &source_pos) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the direction nodes are flowing
|
||||
*/
|
||||
void SetFlowDirection(NodeViewCommon::FlowDirection dir);
|
||||
|
||||
static int DefaultTextPadding();
|
||||
|
||||
static int DefaultItemHeight();
|
||||
|
||||
static int DefaultItemWidth();
|
||||
|
||||
static int DefaultItemBorder();
|
||||
|
||||
qreal DefaultItemHorizontalPadding() const;
|
||||
|
||||
qreal DefaultItemVerticalPadding() const;
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
|
||||
|
||||
virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Highlight an input (for instance when the user is dragging over it)
|
||||
*/
|
||||
void SetHighlightedIndex(int index);
|
||||
|
||||
/**
|
||||
* @brief Returns local rect of a NodeInput in array node_inputs_[index]
|
||||
*/
|
||||
QRectF GetInputRect(int index) const;
|
||||
|
||||
/**
|
||||
* @brief Returns local point that edges should connect to for a NodeInput in array node_inputs_[index]
|
||||
*/
|
||||
QPointF GetInputPoint(int index, const QPointF &source_pos) const;
|
||||
|
||||
/**
|
||||
* @brief Reference to attached Node
|
||||
*/
|
||||
Node* node_;
|
||||
|
||||
/**
|
||||
* @brief Cached list of node inputs
|
||||
*/
|
||||
QList<NodeInput*> node_inputs_;
|
||||
|
||||
/**
|
||||
* @brief Rectangle of the Node's title bar (equal to rect() when collapsed)
|
||||
*/
|
||||
QRectF title_bar_rect_;
|
||||
|
||||
/// Edge dragging variables
|
||||
NodeViewEdge* dragging_edge_;
|
||||
QPointF dragging_edge_start_;
|
||||
NodeParam* drag_src_param_;
|
||||
NodeParam* drag_dest_param_;
|
||||
NodeViewItem* drag_source_;
|
||||
|
||||
NodeViewItem* cached_drop_item_;
|
||||
bool cached_drop_item_expanded_;
|
||||
|
||||
/// Sizing variables to use when drawing
|
||||
int node_border_width_;
|
||||
|
||||
/**
|
||||
* @brief Expanded state
|
||||
*/
|
||||
bool expanded_;
|
||||
|
||||
bool hide_titlebar_;
|
||||
|
||||
/**
|
||||
* @brief Current click mode
|
||||
*
|
||||
* \see mousePressEvent()
|
||||
*/
|
||||
bool standard_click_;
|
||||
|
||||
int highlighted_index_;
|
||||
|
||||
/**
|
||||
* @brief QUndoCommand for creating and deleting edges by dragging
|
||||
*
|
||||
* \see mousePressEvent()
|
||||
* \see mouseReleaseEvent()
|
||||
*/
|
||||
QUndoCommand* node_edge_change_command_;
|
||||
|
||||
NodeViewCommon::FlowDirection flow_dir_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVIEWITEM_H
|
||||
@@ -1,41 +0,0 @@
|
||||
/***
|
||||
|
||||
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 "nodeviewitemwidgetproxy.h"
|
||||
|
||||
QColor NodeViewItemWidget::TitleBarColor()
|
||||
{
|
||||
return title_bar_color_;
|
||||
}
|
||||
|
||||
void NodeViewItemWidget::SetTitleBarColor(QColor color)
|
||||
{
|
||||
title_bar_color_ = color;
|
||||
}
|
||||
|
||||
QColor NodeViewItemWidget::BorderColor()
|
||||
{
|
||||
return border_color_;
|
||||
}
|
||||
|
||||
void NodeViewItemWidget::SetBorderColor(QColor color)
|
||||
{
|
||||
border_color_ = color;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/***
|
||||
|
||||
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 NODEVIEWITEMWIDGETPROXY_H
|
||||
#define NODEVIEWITEMWIDGETPROXY_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
/**
|
||||
* @brief A proxy object to allow NodeViewItem access to CSS functions
|
||||
*
|
||||
* QGraphicsItems can't take Q_PROPERTYs for CSS stylesheet input, but QWidgets can. This is a hack to allow CSS
|
||||
* properties to be set from CSS and then read by NodeViewItem.
|
||||
*/
|
||||
class NodeViewItemWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor titlebarColor READ TitleBarColor WRITE SetTitleBarColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor borderColor READ BorderColor WRITE SetBorderColor DESIGNABLE true)
|
||||
public:
|
||||
NodeViewItemWidget() = default;
|
||||
|
||||
QColor TitleBarColor();
|
||||
void SetTitleBarColor(QColor color);
|
||||
|
||||
QColor BorderColor();
|
||||
void SetBorderColor(QColor color);
|
||||
private:
|
||||
QColor title_bar_color_;
|
||||
QColor border_color_;
|
||||
};
|
||||
|
||||
#endif // NODEVIEWITEMWIDGETPROXY_H
|
||||
@@ -1,313 +0,0 @@
|
||||
/***
|
||||
|
||||
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 "nodeviewscene.h"
|
||||
|
||||
#include "common/functiontimer.h"
|
||||
#include "nodeviewedge.h"
|
||||
#include "nodeviewitem.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeViewScene::NodeViewScene(QObject *parent) :
|
||||
QGraphicsScene(parent),
|
||||
graph_(nullptr),
|
||||
direction_(NodeViewCommon::kLeftToRight),
|
||||
curved_edges_(true)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeViewScene::SetFlowDirection(NodeViewCommon::FlowDirection direction)
|
||||
{
|
||||
direction_ = direction;
|
||||
|
||||
{
|
||||
// Iterate over node items setting direction
|
||||
QHash<Node*, NodeViewItem*>::const_iterator i;
|
||||
for (i=item_map_.constBegin(); i!=item_map_.constEnd(); i++) {
|
||||
i.value()->SetFlowDirection(direction_);
|
||||
|
||||
// Update position too
|
||||
i.value()->SetNodePosition(i.key()->GetPosition());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Iterate over edge items setting direction
|
||||
QHash<NodeEdge*, NodeViewEdge*>::const_iterator i;
|
||||
for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) {
|
||||
i.value()->SetFlowDirection(direction_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewScene::clear()
|
||||
{
|
||||
// Deselect everything (prevents signals that a selection has changed after deleting an object)
|
||||
DeselectAll();
|
||||
|
||||
// HACK: QGraphicsScene contains some sort of internal caching of the selected items which doesn't update unless
|
||||
// we call a function like this. That means even though we deselect all items above, QGraphicsScene will
|
||||
// continue to incorrectly signal selectionChanged() when items that were selected (but are now not) get
|
||||
// deleted. Calling this function appears to update the internal cache and prevent this.
|
||||
selectedItems();
|
||||
|
||||
{
|
||||
QHash<Node*, NodeViewItem*>::const_iterator i;
|
||||
for (i=item_map_.begin();i!=item_map_.end();i++) {
|
||||
delete i.value();
|
||||
}
|
||||
item_map_.clear();
|
||||
}
|
||||
|
||||
{
|
||||
QHash<NodeEdge*, NodeViewEdge*>::const_iterator i;
|
||||
for (i=edge_map_.begin();i!=edge_map_.end();i++) {
|
||||
delete i.value();
|
||||
}
|
||||
edge_map_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewScene::SelectAll()
|
||||
{
|
||||
QList<QGraphicsItem *> all_items = this->items();
|
||||
|
||||
foreach (QGraphicsItem* i, all_items) {
|
||||
i->setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewScene::DeselectAll()
|
||||
{
|
||||
QList<QGraphicsItem *> selected_items = this->selectedItems();
|
||||
|
||||
foreach (QGraphicsItem* i, selected_items) {
|
||||
i->setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
NodeViewItem *NodeViewScene::NodeToUIObject(Node *n)
|
||||
{
|
||||
return item_map_.value(n);
|
||||
}
|
||||
|
||||
NodeViewEdge *NodeViewScene::EdgeToUIObject(NodeEdgePtr n)
|
||||
{
|
||||
return edge_map_.value(n.get());
|
||||
}
|
||||
|
||||
void NodeViewScene::SetGraph(NodeGraph *graph)
|
||||
{
|
||||
graph_ = graph;
|
||||
}
|
||||
|
||||
QVector<Node *> NodeViewScene::GetSelectedNodes() const
|
||||
{
|
||||
QHash<Node*, NodeViewItem*>::const_iterator iterator;
|
||||
QVector<Node *> selected;
|
||||
|
||||
for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) {
|
||||
if (iterator.value()->isSelected()) {
|
||||
selected.append(iterator.key());
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
QVector<NodeViewItem *> NodeViewScene::GetSelectedItems() const
|
||||
{
|
||||
QHash<Node*, NodeViewItem*>::const_iterator iterator;
|
||||
QVector<NodeViewItem *> selected;
|
||||
|
||||
for (iterator=item_map_.begin();iterator!=item_map_.end();iterator++) {
|
||||
if (iterator.value()->isSelected()) {
|
||||
selected.append(iterator.value());
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
QVector<NodeEdge*> NodeViewScene::GetSelectedEdges() const
|
||||
{
|
||||
QVector<NodeEdge*> edges;
|
||||
|
||||
QHash<NodeEdge*, NodeViewEdge*>::const_iterator i;
|
||||
|
||||
for (i=edge_map_.constBegin(); i!=edge_map_.constEnd(); i++) {
|
||||
if (i.value()->isSelected()) {
|
||||
edges.append(i.key());
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
const QHash<Node *, NodeViewItem *> &NodeViewScene::item_map() const
|
||||
{
|
||||
return item_map_;
|
||||
}
|
||||
|
||||
const QHash<NodeEdge *, NodeViewEdge *> &NodeViewScene::edge_map() const
|
||||
{
|
||||
return edge_map_;
|
||||
}
|
||||
|
||||
void NodeViewScene::AddNode(Node* node)
|
||||
{
|
||||
NodeViewItem* item = new NodeViewItem();
|
||||
|
||||
item->SetFlowDirection(direction_);
|
||||
item->SetNode(node);
|
||||
|
||||
addItem(item);
|
||||
item_map_.insert(node, item);
|
||||
|
||||
// Add a NodeViewEdge for each connection
|
||||
foreach (NodeParam* param, node->parameters()) {
|
||||
|
||||
// We only bother working with outputs since eventually this will cover all inputs too
|
||||
// (covering both would lead to duplicates since every edge connects to one input and one output)
|
||||
if (param->type() == NodeParam::kOutput) {
|
||||
const QVector<NodeEdgePtr>& edges = param->edges();
|
||||
|
||||
foreach(NodeEdgePtr edge, edges) {
|
||||
AddEdge(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged);
|
||||
connect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged);
|
||||
}
|
||||
|
||||
void NodeViewScene::RemoveNode(Node *node)
|
||||
{
|
||||
disconnect(node, &Node::LabelChanged, this, &NodeViewScene::NodeLabelChanged);
|
||||
disconnect(node, &Node::PositionChanged, this, &NodeViewScene::NodePositionChanged);
|
||||
|
||||
delete item_map_.take(node);
|
||||
}
|
||||
|
||||
void NodeViewScene::AddEdge(NodeEdgePtr edge)
|
||||
{
|
||||
NodeViewEdge* edge_ui = new NodeViewEdge();
|
||||
|
||||
edge_ui->SetEdge(edge);
|
||||
edge_ui->SetFlowDirection(direction_);
|
||||
edge_ui->SetCurved(curved_edges_);
|
||||
|
||||
addItem(edge_ui);
|
||||
edge_map_.insert(edge.get(), edge_ui);
|
||||
}
|
||||
|
||||
void NodeViewScene::RemoveEdge(NodeEdgePtr edge)
|
||||
{
|
||||
delete edge_map_.take(edge.get());
|
||||
}
|
||||
|
||||
int NodeViewScene::DetermineWeight(Node *n)
|
||||
{
|
||||
QVector<Node*> inputs = n->GetImmediateDependencies();
|
||||
|
||||
int weight = 0;
|
||||
|
||||
foreach (Node* i, inputs) {
|
||||
if (i->GetRoutesTo(n) == 1) {
|
||||
weight += DetermineWeight(i);
|
||||
}
|
||||
}
|
||||
|
||||
return qMax(1, weight);
|
||||
}
|
||||
|
||||
Qt::Orientation NodeViewScene::GetFlowOrientation() const
|
||||
{
|
||||
return NodeViewCommon::GetFlowOrientation(direction_);
|
||||
}
|
||||
|
||||
NodeViewCommon::FlowDirection NodeViewScene::GetFlowDirection() const
|
||||
{
|
||||
return direction_;
|
||||
}
|
||||
|
||||
void NodeViewScene::ReorganizeFrom(Node* n)
|
||||
{
|
||||
QVector<Node*> immediates = n->GetImmediateDependencies();
|
||||
|
||||
if (immediates.isEmpty()) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
QPointF parent_pos = n->GetPosition();
|
||||
|
||||
int weight_count = DetermineWeight(n);
|
||||
|
||||
qreal child_x = parent_pos.x() - 1.0;
|
||||
qreal children_height = weight_count-1;
|
||||
qreal children_y = parent_pos.y() - children_height * 0.5;
|
||||
|
||||
int weight_counter = 0;
|
||||
|
||||
foreach (Node* i, immediates) {
|
||||
if (i->GetRoutesTo(n) == 1) {
|
||||
int weight = DetermineWeight(i);
|
||||
|
||||
i->SetPosition(QPointF(child_x,
|
||||
children_y + weight_counter + (weight - 1) * 0.5));
|
||||
|
||||
weight_counter += weight;
|
||||
|
||||
ReorganizeFrom(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NodeViewScene::GetEdgesAreCurved() const
|
||||
{
|
||||
return curved_edges_;
|
||||
}
|
||||
|
||||
void NodeViewScene::SetEdgesAreCurved(bool curved)
|
||||
{
|
||||
if (curved_edges_ != curved) {
|
||||
curved_edges_ = curved;
|
||||
|
||||
foreach (NodeViewEdge* e, edge_map_) {
|
||||
e->SetCurved(curved_edges_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeViewScene::NodePositionChanged(const QPointF &pos)
|
||||
{
|
||||
item_map_.value(static_cast<Node*>(sender()))->SetNodePosition(pos);
|
||||
}
|
||||
|
||||
void NodeViewScene::NodeLabelChanged()
|
||||
{
|
||||
item_map_.value(static_cast<Node*>(sender()))->update();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/***
|
||||
|
||||
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 NODEVIEWSCENE_H
|
||||
#define NODEVIEWSCENE_H
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QTimer>
|
||||
|
||||
#include "node/graph.h"
|
||||
#include "nodeviewedge.h"
|
||||
#include "nodeviewitem.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class NodeViewScene : public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NodeViewScene(QObject *parent = nullptr);
|
||||
|
||||
void clear();
|
||||
|
||||
void SelectAll();
|
||||
void DeselectAll();
|
||||
|
||||
/**
|
||||
* @brief Retrieve the graphical widget corresponding to a specific Node
|
||||
*
|
||||
* In situations where you know what Node you're working with but need the UI object (e.g. for positioning), this
|
||||
* static function will retrieve the NodeViewItem (Node UI representation) connected to this Node in a certain
|
||||
* QGraphicsScene. This can be called from any other UI object, since it'll have a reference to the QGraphicsScene
|
||||
* through QGraphicsItem::scene().
|
||||
*
|
||||
* If the scene does not contain a widget for this node (usually meaning the node's graph is not the active graph
|
||||
* in this view/scene), this function returns nullptr.
|
||||
*/
|
||||
NodeViewItem* NodeToUIObject(Node* n);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the graphical widget corresponding to a specific NodeEdge
|
||||
*
|
||||
* Same as NodeToUIObject() but returns a NodeViewEdge corresponding to a NodeEdgePtr instead.
|
||||
*/
|
||||
NodeViewEdge* EdgeToUIObject(NodeEdgePtr n);
|
||||
|
||||
void SetGraph(NodeGraph* graph);
|
||||
|
||||
QVector<Node *> GetSelectedNodes() const;
|
||||
QVector<NodeViewItem*> GetSelectedItems() const;
|
||||
QVector<NodeEdge *> GetSelectedEdges() const;
|
||||
|
||||
const QHash<Node*, NodeViewItem*>& item_map() const;
|
||||
const QHash<NodeEdge*, NodeViewEdge*>& edge_map() const;
|
||||
|
||||
Qt::Orientation GetFlowOrientation() const;
|
||||
|
||||
NodeViewCommon::FlowDirection GetFlowDirection() const;
|
||||
void SetFlowDirection(NodeViewCommon::FlowDirection direction);
|
||||
|
||||
/**
|
||||
* @brief Automatically reposition the nodes based on their connections
|
||||
*/
|
||||
void ReorganizeFrom(Node* n);
|
||||
|
||||
bool GetEdgesAreCurved() const;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Slot when a Node is added to a graph (SetGraph() connects this)
|
||||
*
|
||||
* This should NEVER be called directly, only connected to a NodeGraph. To add a Node to the NodeGraph
|
||||
* use NodeGraph::AddNode().
|
||||
*/
|
||||
void AddNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief Slot when a Node is removed from a graph (SetGraph() connects this)
|
||||
*
|
||||
* This should NEVER be called directly, only connected to a NodeGraph. To remove a Node from the NodeGraph
|
||||
* use NodeGraph::RemoveNode().
|
||||
*/
|
||||
void RemoveNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief Slot when an edge is added to a graph (SetGraph() connects this)
|
||||
*
|
||||
* This should NEVER be called directly, only connected to a NodeGraph. To add an edge (i.e. connect two node
|
||||
* parameters together), use NodeParam::ConnectEdge().
|
||||
*/
|
||||
void AddEdge(NodeEdgePtr edge);
|
||||
|
||||
/**
|
||||
* @brief Slot when an edge is removed from a graph (SetGraph() connects this)
|
||||
*
|
||||
* This should NEVER be called directly, only connected to a NodeGraph. To remove an edge (i.e. disconnect two node
|
||||
* parameters), use NodeParam::DisconnectEdge().
|
||||
*/
|
||||
void RemoveEdge(NodeEdgePtr edge);
|
||||
|
||||
/**
|
||||
* @brief Set whether edges in this scene should be curved or not
|
||||
*/
|
||||
void SetEdgesAreCurved(bool curved);
|
||||
|
||||
private:
|
||||
static int DetermineWeight(Node* n);
|
||||
|
||||
QHash<Node*, NodeViewItem*> item_map_;
|
||||
|
||||
QHash<NodeEdge*, NodeViewEdge*> edge_map_;
|
||||
|
||||
NodeGraph* graph_;
|
||||
|
||||
NodeViewCommon::FlowDirection direction_;
|
||||
|
||||
bool curved_edges_;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Receiver for whenever a node position changes
|
||||
*/
|
||||
void NodePositionChanged(const QPointF& pos);
|
||||
|
||||
/**
|
||||
* @brief Receiver for when a node's label has changed
|
||||
*/
|
||||
void NodeLabelChanged();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVIEWSCENE_H
|
||||
@@ -24,87 +24,50 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
NodeEdgeAddCommand::NodeEdgeAddCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) :
|
||||
NodeEdgeAddCommand::NodeEdgeAddCommand(Node *output, NodeInput *input, int element, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
output_(output),
|
||||
input_(input),
|
||||
old_edge_(nullptr),
|
||||
done_(false)
|
||||
element_(element)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeEdgeAddCommand::redo_internal()
|
||||
{
|
||||
if (done_) {
|
||||
return;
|
||||
}
|
||||
|
||||
old_edge_ = NodeParam::DisconnectForNewOutput(input_);
|
||||
|
||||
NodeParam::ConnectEdge(output_, input_);
|
||||
|
||||
done_ = true;
|
||||
Node::ConnectEdge(output_, input_, element_);
|
||||
}
|
||||
|
||||
void NodeEdgeAddCommand::undo_internal()
|
||||
{
|
||||
if (!done_) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeParam::DisconnectEdge(output_, input_);
|
||||
|
||||
if (old_edge_ != nullptr) {
|
||||
NodeParam::ConnectEdge(old_edge_->output(), old_edge_->input());
|
||||
}
|
||||
|
||||
done_ = false;
|
||||
Node::DisconnectEdge(output_, input_, element_);
|
||||
}
|
||||
|
||||
Project *NodeEdgeAddCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(output_->parentNode()->parent())->project();
|
||||
return output_->parent()->project();
|
||||
}
|
||||
|
||||
NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) :
|
||||
NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(Node *output, NodeInput *input, int element, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
output_(output),
|
||||
input_(input),
|
||||
done_(false)
|
||||
{
|
||||
}
|
||||
|
||||
NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(NodeEdgePtr edge, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
output_(edge->output()),
|
||||
input_(edge->input()),
|
||||
done_(false)
|
||||
element_(element)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeEdgeRemoveCommand::redo_internal()
|
||||
{
|
||||
if (done_) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeParam::DisconnectEdge(output_, input_);
|
||||
done_ = true;
|
||||
Node::DisconnectEdge(output_, input_, element_);
|
||||
}
|
||||
|
||||
void NodeEdgeRemoveCommand::undo_internal()
|
||||
{
|
||||
if (!done_) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeParam::ConnectEdge(output_, input_);
|
||||
done_ = false;
|
||||
Node::ConnectEdge(output_, input_, element_);
|
||||
}
|
||||
|
||||
Project *NodeEdgeRemoveCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(output_->parentNode()->parent())->project();
|
||||
return output_->parent()->project();
|
||||
}
|
||||
|
||||
NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node, QUndoCommand *parent) :
|
||||
@@ -118,75 +81,34 @@ NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node, QUndoCommand *paren
|
||||
|
||||
void NodeAddCommand::redo_internal()
|
||||
{
|
||||
graph_->AddNode(node_);
|
||||
node_->setParent(graph_);
|
||||
}
|
||||
|
||||
void NodeAddCommand::undo_internal()
|
||||
{
|
||||
graph_->TakeNode(node_, &memory_manager_);
|
||||
node_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
Project *NodeAddCommand::GetRelevantProject() const
|
||||
{
|
||||
return static_cast<Sequence*>(graph_)->project();
|
||||
return graph_->project();
|
||||
}
|
||||
|
||||
NodeRemoveCommand::NodeRemoveCommand(NodeGraph *graph, const QVector<Node *> &nodes, QUndoCommand *parent) :
|
||||
NodeRemoveCommand::NodeRemoveCommand(Node *node, QUndoCommand *parent) :
|
||||
UndoCommand(parent),
|
||||
graph_(graph),
|
||||
nodes_(nodes)
|
||||
graph_(node->parent()),
|
||||
node_(node)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeRemoveCommand::redo_internal()
|
||||
{
|
||||
// Cache edges for undoing
|
||||
foreach (Node* n, nodes_) {
|
||||
foreach (NodeParam* param, n->parameters()) {
|
||||
foreach (NodeEdgePtr edge, param->edges()) {
|
||||
// Ensures the same edge isn't added twice (prevents double connecting when undoing)
|
||||
if (!edges_.contains(edge)) {
|
||||
edges_.append(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Take nodes from graph (TakeNode() will automatically disconnect edges)
|
||||
foreach (Node* n, nodes_) {
|
||||
// If the node is a block, unlink any linked blocks before removing
|
||||
if (n->IsBlock()) {
|
||||
Block *b = static_cast<Block *>(n);
|
||||
if (b->HasLinks()) {
|
||||
BlockUnlinkAllCommand *unlink_command = new BlockUnlinkAllCommand(b);
|
||||
unlink_command->redo();
|
||||
block_unlink_commands_.append(unlink_command);
|
||||
}
|
||||
}
|
||||
graph_->TakeNode(n, &memory_manager_);
|
||||
}
|
||||
node_->setParent(&memory_manager_);
|
||||
}
|
||||
|
||||
void NodeRemoveCommand::undo_internal()
|
||||
{
|
||||
// Re-add nodes to graph
|
||||
foreach (Node* n, nodes_) {
|
||||
graph_->AddNode(n);
|
||||
}
|
||||
|
||||
// Relink any blocks that were unlinked
|
||||
foreach(BlockUnlinkAllCommand* command, block_unlink_commands_) {
|
||||
command->undo();
|
||||
delete command;
|
||||
}
|
||||
|
||||
// Re-connect edges
|
||||
foreach (NodeEdgePtr edge, edges_) {
|
||||
NodeParam::ConnectEdge(edge->output(), edge->input());
|
||||
}
|
||||
|
||||
edges_.clear();
|
||||
block_unlink_commands_.clear();
|
||||
node_->setParent(graph_);
|
||||
}
|
||||
|
||||
Project *NodeRemoveCommand::GetRelevantProject() const
|
||||
@@ -194,21 +116,6 @@ Project *NodeRemoveCommand::GetRelevantProject() const
|
||||
return static_cast<Sequence*>(graph_)->project();
|
||||
}
|
||||
|
||||
NodeRemoveWithExclusiveDeps::NodeRemoveWithExclusiveDeps(NodeGraph *graph, Node *node, QUndoCommand *parent) :
|
||||
UndoCommand(parent)
|
||||
{
|
||||
QVector<Node*> node_and_its_deps;
|
||||
node_and_its_deps.append(node);
|
||||
node_and_its_deps.append(node->GetExclusiveDependencies());
|
||||
|
||||
remove_command_ = new NodeRemoveCommand(graph, node_and_its_deps, this);
|
||||
}
|
||||
|
||||
Project *NodeRemoveWithExclusiveDeps::GetRelevantProject() const
|
||||
{
|
||||
return remove_command_->GetRelevantProject();
|
||||
}
|
||||
|
||||
NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include_connections, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
src_(src),
|
||||
@@ -217,14 +124,6 @@ NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include
|
||||
{
|
||||
}
|
||||
|
||||
NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
src_(src),
|
||||
dest_(dest),
|
||||
include_connections_(true)
|
||||
{
|
||||
}
|
||||
|
||||
void NodeCopyInputsCommand::redo()
|
||||
{
|
||||
Node::CopyInputs(src_, dest_, include_connections_);
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#include "node/graph.h"
|
||||
#include "node/node.h"
|
||||
#include "nodeviewitem.h"
|
||||
#include "undo/undocommand.h"
|
||||
#include "widget/timelinewidget/undo/undo.h"
|
||||
|
||||
@@ -38,7 +37,7 @@ namespace olive {
|
||||
*/
|
||||
class NodeEdgeAddCommand : public UndoCommand {
|
||||
public:
|
||||
NodeEdgeAddCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr);
|
||||
NodeEdgeAddCommand(Node* output, NodeInput* input, int element, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -47,12 +46,10 @@ protected:
|
||||
virtual void undo_internal() override;
|
||||
|
||||
private:
|
||||
NodeOutput* output_;
|
||||
Node* output_;
|
||||
NodeInput* input_;
|
||||
int element_;
|
||||
|
||||
NodeEdgePtr old_edge_;
|
||||
|
||||
bool done_;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -62,8 +59,7 @@ private:
|
||||
*/
|
||||
class NodeEdgeRemoveCommand : public UndoCommand {
|
||||
public:
|
||||
NodeEdgeRemoveCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr);
|
||||
NodeEdgeRemoveCommand(NodeEdgePtr edge, QUndoCommand* parent = nullptr);
|
||||
NodeEdgeRemoveCommand(Node* output, NodeInput* input, int element, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -72,10 +68,10 @@ protected:
|
||||
virtual void undo_internal() override;
|
||||
|
||||
private:
|
||||
NodeOutput* output_;
|
||||
Node* output_;
|
||||
NodeInput* input_;
|
||||
int element_;
|
||||
|
||||
bool done_;
|
||||
};
|
||||
|
||||
class NodeAddCommand : public UndoCommand {
|
||||
@@ -97,8 +93,7 @@ private:
|
||||
|
||||
class NodeRemoveCommand : public UndoCommand {
|
||||
public:
|
||||
NodeRemoveCommand(NodeGraph* graph,
|
||||
const QVector<Node*>& nodes,
|
||||
NodeRemoveCommand(Node* node,
|
||||
QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
@@ -111,32 +106,15 @@ private:
|
||||
QObject memory_manager_;
|
||||
|
||||
NodeGraph* graph_;
|
||||
QVector<Node*> nodes_;
|
||||
QVector<NodeEdgePtr> edges_;
|
||||
QVector<BlockUnlinkAllCommand*> block_unlink_commands_;
|
||||
};
|
||||
Node* node_;
|
||||
|
||||
class NodeRemoveWithExclusiveDeps : public UndoCommand {
|
||||
public:
|
||||
NodeRemoveWithExclusiveDeps(NodeGraph* graph,
|
||||
Node* node,
|
||||
QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
private:
|
||||
NodeRemoveCommand* remove_command_;
|
||||
};
|
||||
|
||||
class NodeCopyInputsCommand : public QUndoCommand {
|
||||
public:
|
||||
NodeCopyInputsCommand(Node* src,
|
||||
Node* dest,
|
||||
bool include_connections = true,
|
||||
QUndoCommand* parent = nullptr);
|
||||
|
||||
NodeCopyInputsCommand(Node* src,
|
||||
Node* dest,
|
||||
bool include_connections,
|
||||
QUndoCommand* parent = nullptr);
|
||||
|
||||
protected:
|
||||
@@ -151,64 +129,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class NodeGraphBeginOperationCommand : public UndoCommand {
|
||||
public:
|
||||
NodeGraphBeginOperationCommand(NodeGraph* graph, QUndoCommand* parent = nullptr) :
|
||||
UndoCommand(parent),
|
||||
graph_(graph)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return static_cast<Sequence*>(graph_)->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo_internal() override
|
||||
{
|
||||
graph_->BeginOperation();
|
||||
}
|
||||
|
||||
virtual void undo_internal() override
|
||||
{
|
||||
graph_->EndOperation();
|
||||
}
|
||||
|
||||
private:
|
||||
NodeGraph* graph_;
|
||||
|
||||
};
|
||||
|
||||
class NodeGraphEndOperationCommand : public UndoCommand {
|
||||
public:
|
||||
NodeGraphEndOperationCommand(NodeGraph* graph, QUndoCommand* parent = nullptr) :
|
||||
UndoCommand(parent),
|
||||
graph_(graph)
|
||||
{
|
||||
}
|
||||
|
||||
virtual Project* GetRelevantProject() const override
|
||||
{
|
||||
return static_cast<Sequence*>(graph_)->project();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void redo_internal() override
|
||||
{
|
||||
graph_->EndOperation();
|
||||
}
|
||||
|
||||
virtual void undo_internal() override
|
||||
{
|
||||
graph_->BeginOperation();
|
||||
}
|
||||
|
||||
private:
|
||||
NodeGraph* graph_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // NODEVIEWUNDO_H
|
||||
|
||||
@@ -541,9 +541,9 @@ void ProjectExplorer::DeselectAll()
|
||||
CurrentView()->selectionModel()->clearSelection();
|
||||
}
|
||||
|
||||
QList<MediaInput *> ProjectExplorer::GetMediaNodesUsingFootage(Footage *item)
|
||||
QVector<MediaInput *> ProjectExplorer::GetMediaNodesUsingFootage(Footage *item)
|
||||
{
|
||||
QList<MediaInput *> list;
|
||||
QVector<MediaInput *> list;
|
||||
|
||||
// Get all sequences.
|
||||
QVector<Item*> sequences = model_.project()->get_items_of_type(Item::kSequence);
|
||||
@@ -552,12 +552,10 @@ QList<MediaInput *> ProjectExplorer::GetMediaNodesUsingFootage(Footage *item)
|
||||
foreach (Item* s, sequences) {
|
||||
const QList<Node*>& nodes = static_cast<Sequence*>(s)->nodes();
|
||||
foreach (Node* n, nodes) {
|
||||
if (n->IsMedia()) {
|
||||
MediaInput* media_node = static_cast<MediaInput*>(n);
|
||||
MediaInput* media_node = dynamic_cast<MediaInput*>(n);
|
||||
|
||||
if (media_node->stream()->footage() == item) {
|
||||
list.append(media_node);
|
||||
}
|
||||
if (media_node && media_node->stream()->footage() == item) {
|
||||
list.append(media_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -593,7 +591,7 @@ void ProjectExplorer::DeleteSelected()
|
||||
// If this is footage, check if it's used anywhere in any sequence
|
||||
Footage* footage = static_cast<Footage*>(item);
|
||||
|
||||
QList<MediaInput*> footage_nodes = GetMediaNodesUsingFootage(footage);
|
||||
QVector<MediaInput*> footage_nodes = GetMediaNodesUsingFootage(footage);
|
||||
|
||||
if (!footage_nodes.isEmpty()) {
|
||||
// Footage is in use, show messagebox asking what to do about it
|
||||
|
||||
@@ -105,11 +105,11 @@ private:
|
||||
/**
|
||||
* @brief Check if an item is in use anywhere and return any relevant input nodes
|
||||
*/
|
||||
QList<MediaInput*> GetMediaNodesUsingFootage(Footage* item);
|
||||
QVector<MediaInput*> GetMediaNodesUsingFootage(Footage* item);
|
||||
|
||||
/**
|
||||
* @brief Get all the blocks that solely rely on an input node
|
||||
*
|
||||
*
|
||||
* Ignores blocks that depend on multiple inputs
|
||||
*/
|
||||
QList<Block*> GetFootageBlocks(QList<Node*> nodes);
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
OfflineFootageCommand::OfflineFootageCommand(const QList<MediaInput *> &media, QUndoCommand* parent) :
|
||||
OfflineFootageCommand::OfflineFootageCommand(const QVector<MediaInput *> &media, QUndoCommand* parent) :
|
||||
UndoCommand(parent)
|
||||
{
|
||||
foreach (MediaInput* i, media) {
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace olive {
|
||||
*/
|
||||
class OfflineFootageCommand : public UndoCommand {
|
||||
public:
|
||||
OfflineFootageCommand(const QList<MediaInput*>& media, QUndoCommand* parent = nullptr);
|
||||
OfflineFootageCommand(const QVector<MediaInput*>& media, QUndoCommand* parent = nullptr);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
|
||||
@@ -73,9 +73,9 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
|
||||
ShaderJob shader_job;
|
||||
|
||||
shader_job.InsertValue(QStringLiteral("viewport"), ShaderValue(QVector2D(width(), height()), NodeParam::kVec2));
|
||||
shader_job.InsertValue(QStringLiteral("histogram_scale"), ShaderValue(histogram_scale, NodeParam::kFloat));
|
||||
shader_job.InsertValue(QStringLiteral("histogram_power"), ShaderValue(histogram_power, NodeParam::kFloat));
|
||||
shader_job.InsertValue(QStringLiteral("viewport"), NodeValue(NodeValue::kVec2, QVector2D(width(), height())));
|
||||
shader_job.InsertValue(QStringLiteral("histogram_scale"), NodeValue(NodeValue::kFloat, histogram_scale));
|
||||
shader_job.InsertValue(QStringLiteral("histogram_power"), NodeValue(NodeValue::kFloat, histogram_power));
|
||||
|
||||
if (!texture_row_sums_
|
||||
|| texture_row_sums_->width() != this->width()
|
||||
@@ -86,11 +86,11 @@ void HistogramScope::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
}
|
||||
|
||||
// Draw managed texture to a sums texture
|
||||
shader_job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(managed_tex), NodeParam::kTexture));
|
||||
shader_job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex)));
|
||||
renderer()->BlitToTexture(pipeline, shader_job, texture_row_sums_.get());
|
||||
|
||||
// Draw sums into a histogram
|
||||
shader_job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(texture_row_sums_), NodeParam::kTexture));
|
||||
shader_job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_row_sums_)));
|
||||
renderer()->Blit(pipeline_secondary_, shader_job, texture_row_sums_->params());
|
||||
|
||||
// Draw line overlays
|
||||
|
||||
@@ -54,7 +54,7 @@ void ScopeBase::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
{
|
||||
ShaderJob job;
|
||||
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(managed_tex), NodeParam::kTexture));
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex)));
|
||||
|
||||
renderer()->Blit(pipeline, job, VideoParams(width(), height(),
|
||||
static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt()),
|
||||
|
||||
@@ -58,22 +58,22 @@ void WaveformScope::DrawScope(TexturePtr managed_tex, QVariant pipeline)
|
||||
|
||||
// Set viewport size
|
||||
job.InsertValue(QStringLiteral("viewport"),
|
||||
ShaderValue(QVector2D(width(), height()), NodeParam::kVec2));
|
||||
NodeValue(NodeValue::kVec2, QVector2D(width(), height())));
|
||||
|
||||
// Set luma coefficients
|
||||
double luma_coeffs[3] = {0.0f, 0.0f, 0.0f};
|
||||
color_manager()->GetDefaultLumaCoefs(luma_coeffs);
|
||||
job.InsertValue(QStringLiteral("luma_coeffs"),
|
||||
ShaderValue(QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2]), NodeParam::kVec3));
|
||||
NodeValue(NodeValue::kVec3, QVector3D(luma_coeffs[0], luma_coeffs[1], luma_coeffs[2])));
|
||||
|
||||
|
||||
// Scale of the waveform relative to the viewport surface.
|
||||
job.InsertValue(QStringLiteral("waveform_scale"),
|
||||
ShaderValue(waveform_scale, NodeParam::kFloat));
|
||||
NodeValue(NodeValue::kFloat, waveform_scale));
|
||||
|
||||
// Insert source texture
|
||||
job.InsertValue(QStringLiteral("ove_maintex"),
|
||||
ShaderValue(QVariant::fromValue(managed_tex), NodeParam::kTexture));
|
||||
NodeValue(NodeValue::kTexture, QVariant::fromValue(managed_tex)));
|
||||
|
||||
renderer()->Blit(pipeline, job, VideoParams(width(), height(),
|
||||
static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt()),
|
||||
|
||||
@@ -469,7 +469,7 @@ void TimelineWidget::ReplaceBlocksWithGaps(const QVector<Block *> &blocks,
|
||||
new TrackReplaceBlockWithGapCommand(original_track, b, command);
|
||||
|
||||
if (remove_from_graph) {
|
||||
new NodeRemoveWithExclusiveDeps(static_cast<NodeGraph*>(b->parent()), b, command);
|
||||
Node::RemoveNodesAndExclusiveDependencies(b, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,9 +509,7 @@ void TimelineWidget::DeleteSelected(bool ripple)
|
||||
transition,
|
||||
command);
|
||||
|
||||
new NodeRemoveWithExclusiveDeps(static_cast<NodeGraph*>(GetConnectedNode()->parent()),
|
||||
transition,
|
||||
command);
|
||||
Node::RemoveNodesAndExclusiveDependencies(transition, command);
|
||||
}
|
||||
|
||||
// Replace clips with gaps (effectively deleting them)
|
||||
@@ -910,37 +908,28 @@ void TimelineWidget::AddBlock(Block *block, TrackReference track)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::RemoveBlock(const QList<Block *> &blocks)
|
||||
void TimelineWidget::RemoveBlock(Block *b)
|
||||
{
|
||||
QVector<Block*> deselect_blocks;
|
||||
// Disconnect all signals
|
||||
disconnect(b, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed);
|
||||
disconnect(b, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated);
|
||||
disconnect(b, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated);
|
||||
disconnect(b, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated);
|
||||
|
||||
foreach (Block* b, blocks) {
|
||||
// Disconnect all signals
|
||||
disconnect(b, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed);
|
||||
disconnect(b, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated);
|
||||
disconnect(b, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated);
|
||||
disconnect(b, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated);
|
||||
// Take item from map
|
||||
TimelineViewBlockItem* item = block_items_.take(b);
|
||||
|
||||
// Take item from map
|
||||
TimelineViewBlockItem* item = block_items_.take(b);
|
||||
|
||||
// If selected, deselect it
|
||||
int select_index = selected_blocks_.indexOf(b);
|
||||
if (select_index > -1) {
|
||||
selected_blocks_.removeAt(select_index);
|
||||
deselect_blocks.append(b);
|
||||
RemoveSelection(item);
|
||||
}
|
||||
|
||||
// Finally, delete item
|
||||
delete item;
|
||||
// If selected, deselect it
|
||||
int select_index = selected_blocks_.indexOf(b);
|
||||
if (select_index > -1) {
|
||||
selected_blocks_.removeAt(select_index);
|
||||
RemoveSelection(item);
|
||||
}
|
||||
|
||||
if (!deselect_blocks.isEmpty()) {
|
||||
// We already removed the blocks from selected_blocks_, so we can signal directly rather than
|
||||
// through
|
||||
emit BlocksDeselected(deselect_blocks);
|
||||
}
|
||||
// Finally, delete item
|
||||
delete item;
|
||||
|
||||
emit BlocksDeselected({b});
|
||||
}
|
||||
|
||||
void TimelineWidget::AddTrack(TrackOutput *track, Timeline::TrackType type)
|
||||
@@ -958,7 +947,9 @@ void TimelineWidget::RemoveTrack(TrackOutput *track)
|
||||
disconnect(track, &TrackOutput::IndexChanged, this, &TimelineWidget::TrackIndexChanged);
|
||||
disconnect(track, &TrackOutput::PreviewChanged, this, &TimelineWidget::TrackPreviewUpdated);
|
||||
|
||||
RemoveBlock(track->Blocks());
|
||||
foreach (Block* b, track->Blocks()) {
|
||||
RemoveBlock(b);
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::TrackIndexChanged()
|
||||
|
||||
@@ -281,7 +281,7 @@ private slots:
|
||||
void ViewDragDropped(TimelineViewMouseEvent* event);
|
||||
|
||||
void AddBlock(Block* block, TrackReference track);
|
||||
void RemoveBlock(const QList<Block*>& blocks);
|
||||
void RemoveBlock(Block *blocks);
|
||||
|
||||
void AddTrack(TrackOutput* track, Timeline::TrackType type);
|
||||
void RemoveTrack(TrackOutput* track);
|
||||
|
||||
@@ -123,7 +123,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
solid,
|
||||
command);
|
||||
|
||||
new NodeEdgeAddCommand(solid->output(), clip->texture_input(), command);
|
||||
new NodeEdgeAddCommand(solid, clip->texture_input(), -1, command);
|
||||
break;
|
||||
}
|
||||
case olive::Tool::kAddableTitle:
|
||||
@@ -134,7 +134,7 @@ void AddTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
text,
|
||||
command);
|
||||
|
||||
new NodeEdgeAddCommand(text->output(), clip->texture_input(), command);
|
||||
new NodeEdgeAddCommand(text, clip->texture_input(), -1, command);
|
||||
break;
|
||||
}
|
||||
case olive::Tool::kAddableBars:
|
||||
|
||||
@@ -435,8 +435,8 @@ void ImportTool::DropGhosts(bool insert)
|
||||
TransformDistortNode* transform = new TransformDistortNode();
|
||||
new NodeAddCommand(dst_graph, transform, command);
|
||||
|
||||
new NodeEdgeAddCommand(video_input->output(), transform->texture_input(), command);
|
||||
new NodeEdgeAddCommand(transform->output(), clip->texture_input(), command);
|
||||
new NodeEdgeAddCommand(video_input, transform->texture_input(), -1, command);
|
||||
new NodeEdgeAddCommand(transform, clip->texture_input(), -1, command);
|
||||
|
||||
/*
|
||||
MatrixGenerator* matrix = new MatrixGenerator();
|
||||
@@ -461,8 +461,8 @@ void ImportTool::DropGhosts(bool insert)
|
||||
VolumeNode* volume_node = new VolumeNode();
|
||||
new NodeAddCommand(dst_graph, volume_node, command);
|
||||
|
||||
new NodeEdgeAddCommand(audio_input->output(), volume_node->samples_input(), command);
|
||||
new NodeEdgeAddCommand(volume_node->output(), clip->texture_input(), command);
|
||||
new NodeEdgeAddCommand(audio_input, volume_node->samples_input(), -1, command);
|
||||
new NodeEdgeAddCommand(volume_node, clip->texture_input(), -1, command);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -572,8 +572,6 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
new NodeGraphBeginOperationCommand(static_cast<NodeGraph*>(parent()->GetConnectedNode()->parent()), command);
|
||||
|
||||
if (!blocks_trimming.isEmpty()) {
|
||||
foreach (const GhostBlockPair& p, blocks_trimming) {
|
||||
TimelineViewGhostItem* ghost = p.ghost;
|
||||
@@ -730,8 +728,6 @@ void PointerTool::FinishDrag(TimelineViewMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
new NodeGraphEndOperationCommand(static_cast<NodeGraph*>(parent()->GetConnectedNode()->parent()), command);
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
|
||||
@@ -147,12 +147,14 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
Block* in_block = (ghost_->GetMode() == Timeline::kTrimIn) ? active_block : friend_block;
|
||||
|
||||
// Connect block to transition
|
||||
new NodeEdgeAddCommand(out_block->output(),
|
||||
new NodeEdgeAddCommand(out_block,
|
||||
transition->out_block_input(),
|
||||
-1,
|
||||
command);
|
||||
|
||||
new NodeEdgeAddCommand(in_block->output(),
|
||||
new NodeEdgeAddCommand(in_block,
|
||||
transition->in_block_input(),
|
||||
-1,
|
||||
command);
|
||||
} else {
|
||||
Block* block_to_transition = Node::ValueToPtr<Block>(ghost_->GetData(TimelineViewGhostItem::kAttachedBlock));
|
||||
@@ -167,8 +169,9 @@ void TransitionTool::MouseRelease(TimelineViewMouseEvent *event)
|
||||
}
|
||||
|
||||
// Connect block to transition
|
||||
new NodeEdgeAddCommand(block_to_transition->output(),
|
||||
new NodeEdgeAddCommand(block_to_transition,
|
||||
transition_input_to_connect,
|
||||
-1,
|
||||
command);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,6 @@
|
||||
|
||||
namespace olive {
|
||||
|
||||
Node* TakeNodeFromParentGraph(Node* n, QObject* new_parent = nullptr)
|
||||
{
|
||||
static_cast<NodeGraph*>(n->parent())->TakeNode(n, new_parent);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
BlockResizeCommand::BlockResizeCommand(Block *block, rational new_length, QUndoCommand* parent) :
|
||||
UndoCommand(parent),
|
||||
block_(block),
|
||||
@@ -266,7 +259,8 @@ void TrackRippleRemoveAreaCommand::redo_internal()
|
||||
foreach (Block* remove_block, removed_blocks_) {
|
||||
track_->RippleRemoveBlock(remove_block);
|
||||
|
||||
NodeRemoveWithExclusiveDeps* remove_command = new NodeRemoveWithExclusiveDeps(static_cast<NodeGraph*>(remove_block->parent()), remove_block);
|
||||
QUndoCommand* remove_command = new QUndoCommand();
|
||||
Node::RemoveNodesAndExclusiveDependencies(remove_block, remove_command);
|
||||
remove_command->redo();
|
||||
remove_block_commands_.append(remove_command);
|
||||
}
|
||||
@@ -295,7 +289,6 @@ void TrackRippleRemoveAreaCommand::redo_internal()
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX),
|
||||
track_->block_input(),
|
||||
track_->block_input());
|
||||
}
|
||||
|
||||
@@ -334,7 +327,7 @@ void TrackRippleRemoveAreaCommand::undo_internal()
|
||||
|
||||
// Restore blocks that were removed
|
||||
for (int i=remove_block_commands_.size()-1;i>=0;i--) {
|
||||
UndoCommand* command = remove_block_commands_.at(i);
|
||||
QUndoCommand* command = remove_block_commands_.at(i);
|
||||
command->undo();
|
||||
delete command;
|
||||
}
|
||||
@@ -361,7 +354,7 @@ void TrackRippleRemoveAreaCommand::undo_internal()
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(TimeRange(in_, insert_ ? out_ : RATIONAL_MAX),
|
||||
track_->block_input(), track_->block_input());
|
||||
track_->block_input());
|
||||
|
||||
if (splice_split_command_) {
|
||||
delete splice_split_command_;
|
||||
@@ -385,13 +378,24 @@ Project *TrackPlaceBlockCommand::GetRelevantProject() const
|
||||
|
||||
void TrackPlaceBlockCommand::redo_internal()
|
||||
{
|
||||
added_track_count_ = 0;
|
||||
// Determine if we need to add tracks
|
||||
if (track_index_ >= timeline_->GetTracks().size()) {
|
||||
if (added_tracks_.isEmpty()) {
|
||||
// First redo, create tracks now
|
||||
added_tracks_.resize(track_index_ - timeline_->GetTracks().size() + 1);
|
||||
|
||||
// Get track (or make it if necessary)
|
||||
while (track_index_ >= timeline_->GetTracks().size()) {
|
||||
timeline_->AddTrack();
|
||||
for (int i=0; i<added_tracks_.size(); i++) {
|
||||
added_tracks_[i] = new TrackOutput();
|
||||
}
|
||||
}
|
||||
|
||||
added_track_count_++;
|
||||
for (int i=0; i<added_tracks_.size(); i++) {
|
||||
TrackOutput* track = added_tracks_.at(i);
|
||||
track->setParent(timeline_->GetParentGraph());
|
||||
|
||||
timeline_->track_input()->ArrayAppend();
|
||||
Node::ConnectEdge(track, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
track_ = timeline_->GetTrackAt(track_index_);
|
||||
@@ -404,7 +408,7 @@ void TrackPlaceBlockCommand::redo_internal()
|
||||
// If so, insert a gap here
|
||||
gap_ = new GapBlock();
|
||||
gap_->set_length_and_media_out(in_ - track_->track_length());
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(gap_);
|
||||
gap_->setParent(track_->parent());
|
||||
track_->AppendBlock(gap_);
|
||||
}
|
||||
|
||||
@@ -424,14 +428,17 @@ void TrackPlaceBlockCommand::undo_internal()
|
||||
|
||||
if (gap_ != nullptr) {
|
||||
track_->RippleRemoveBlock(gap_);
|
||||
TakeNodeFromParentGraph(gap_, &memory_manager_);
|
||||
gap_->setParent(&memory_manager_);
|
||||
}
|
||||
} else {
|
||||
TrackRippleRemoveAreaCommand::undo_internal();
|
||||
}
|
||||
|
||||
for (;added_track_count_>0;added_track_count_--) {
|
||||
timeline_->RemoveTrack(&memory_manager_);
|
||||
for (int i=added_tracks_.size()-1; i>=0; i--) {
|
||||
TrackOutput* track = added_tracks_.at(i);
|
||||
Node::DisconnectEdge(track, timeline_->track_input(), timeline_->track_input()->ArraySize() - 1);
|
||||
track->setParent(&memory_manager_);
|
||||
timeline_->track_input()->ArrayRemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,13 +472,16 @@ void BlockSplitCommand::redo_internal()
|
||||
{
|
||||
track_->BeginOperation();
|
||||
|
||||
NodeGraph* graph = static_cast<NodeGraph*>(block_->parent());
|
||||
NodeGraph* graph = block_->parent();
|
||||
|
||||
add_command_ = new QUndoCommand();
|
||||
new NodeAddCommand(graph, new_block_, add_command_);
|
||||
new NodeCopyInputsCommand(block_, new_block_, true, add_command_);
|
||||
|
||||
if (Config::Current()[QStringLiteral("SplitClipsCopyNodes")].toBool()) {
|
||||
bool copy_dependencies_too = Config::Current()[QStringLiteral("SplitClipsCopyNodes")].toBool();
|
||||
|
||||
new NodeCopyInputsCommand(block_, new_block_, !copy_dependencies_too, add_command_);
|
||||
|
||||
if (copy_dependencies_too) {
|
||||
|
||||
QVector<Node*> src_nodes;
|
||||
QVector<Node*> dst_nodes;
|
||||
@@ -502,8 +512,8 @@ void BlockSplitCommand::redo_internal()
|
||||
track_->InsertBlockAfter(new_block_, block_);
|
||||
|
||||
foreach (NodeInput* transition, transitions_to_move_) {
|
||||
NodeParam::DisconnectEdge(block_->output(), transition);
|
||||
NodeParam::ConnectEdge(new_block_->output(), transition);
|
||||
Node::DisconnectEdge(block_, transition);
|
||||
Node::ConnectEdge(new_block_, transition);
|
||||
}
|
||||
|
||||
track_->EndOperation();
|
||||
@@ -514,8 +524,8 @@ void BlockSplitCommand::undo_internal()
|
||||
track_->BeginOperation();
|
||||
|
||||
foreach (NodeInput* transition, transitions_to_move_) {
|
||||
NodeParam::DisconnectEdge(new_block_->output(), transition);
|
||||
NodeParam::ConnectEdge(block_->output(), transition);
|
||||
Node::DisconnectEdge(new_block_, transition);
|
||||
Node::ConnectEdge(block_, transition);
|
||||
}
|
||||
|
||||
block_->set_length_and_media_out(old_length_);
|
||||
@@ -929,7 +939,7 @@ void BlockTrimCommand::redo_internal()
|
||||
|
||||
adjacent_ = new GapBlock();
|
||||
adjacent_->set_length_and_media_out(trim_diff);
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(adjacent_);
|
||||
adjacent_->setParent(track_->parent());
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
track_->InsertBlockBefore(adjacent_, block_);
|
||||
@@ -948,7 +958,7 @@ void BlockTrimCommand::redo_internal()
|
||||
if (adjacent_length.isNull()) {
|
||||
// Ripple remove block
|
||||
track_->RippleRemoveBlock(adjacent_);
|
||||
TakeNodeFromParentGraph(adjacent_, &memory_manager_);
|
||||
adjacent_->setParent(&memory_manager_);
|
||||
we_deleted_adjacent_ = true;
|
||||
} else if (mode_ == Timeline::kTrimIn) {
|
||||
adjacent_->set_length_and_media_out(adjacent_length);
|
||||
@@ -965,7 +975,7 @@ void BlockTrimCommand::redo_internal()
|
||||
invalidate_range = TimeRange(block_->in(), block_->out());
|
||||
}
|
||||
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input(), track_->block_input());
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input());
|
||||
}
|
||||
|
||||
void BlockTrimCommand::undo_internal()
|
||||
@@ -987,7 +997,7 @@ void BlockTrimCommand::undo_internal()
|
||||
if (we_created_adjacent_) {
|
||||
// If we created a gap, just remove it straight up
|
||||
track_->RippleRemoveBlock(adjacent_);
|
||||
TakeNodeFromParentGraph(adjacent_, &memory_manager_);
|
||||
adjacent_->setParent(&memory_manager_);
|
||||
adjacent_ = nullptr;
|
||||
we_created_adjacent_ = false;
|
||||
} else if (adjacent_) {
|
||||
@@ -999,7 +1009,7 @@ void BlockTrimCommand::undo_internal()
|
||||
// If trimmed LONGER, we adjusted an existing block
|
||||
// (assume if there's no adjacent, we're at the end of the timeline and do nothing)
|
||||
if (we_deleted_adjacent_) {
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(adjacent_);
|
||||
adjacent_->setParent(track_->parent());
|
||||
|
||||
if (mode_ == Timeline::kTrimIn) {
|
||||
track_->InsertBlockBefore(adjacent_, block_);
|
||||
@@ -1032,7 +1042,7 @@ void BlockTrimCommand::undo_internal()
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input(), track_->block_input());
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input());
|
||||
}
|
||||
|
||||
TrackReplaceBlockWithGapCommand::TrackReplaceBlockWithGapCommand(TrackOutput *track, Block *block, QUndoCommand *command) :
|
||||
@@ -1074,7 +1084,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal()
|
||||
existing_merged_gap_ = static_cast<GapBlock*>(next);
|
||||
new_gap_length += existing_merged_gap_->length();
|
||||
track_->RippleRemoveBlock(existing_merged_gap_);
|
||||
TakeNodeFromParentGraph(existing_merged_gap_, &memory_manager_);
|
||||
existing_merged_gap_->setParent(&memory_manager_);
|
||||
} else if (previous_is_a_gap) {
|
||||
// Extend this gap to fill space left by block
|
||||
existing_gap_ = static_cast<GapBlock*>(previous);
|
||||
@@ -1094,7 +1104,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal()
|
||||
// No gap exists to fill this space, create a new one and swap it in
|
||||
our_gap_ = new GapBlock();
|
||||
our_gap_->set_length_and_media_out(new_gap_length);
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(our_gap_);
|
||||
our_gap_->setParent(track_->parent());
|
||||
track_->ReplaceBlock(block_, our_gap_);
|
||||
}
|
||||
|
||||
@@ -1105,7 +1115,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal()
|
||||
Block* preceding = block_->previous();
|
||||
if (preceding && preceding->type() == Block::kGap) {
|
||||
track_->RippleRemoveBlock(preceding);
|
||||
TakeNodeFromParentGraph(preceding, &memory_manager_);
|
||||
preceding->setParent(&memory_manager_);
|
||||
|
||||
existing_merged_gap_ = static_cast<GapBlock*>(preceding);
|
||||
}
|
||||
@@ -1116,7 +1126,7 @@ void TrackReplaceBlockWithGapCommand::redo_internal()
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input(), track_->block_input());
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input());
|
||||
}
|
||||
|
||||
void TrackReplaceBlockWithGapCommand::undo_internal()
|
||||
@@ -1127,7 +1137,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal()
|
||||
|
||||
// We made this gap, simply swap our gap back
|
||||
track_->ReplaceBlock(our_gap_, block_);
|
||||
TakeNodeFromParentGraph(our_gap_, &memory_manager_);
|
||||
our_gap_->setParent(&memory_manager_);
|
||||
our_gap_ = nullptr;
|
||||
|
||||
} else if (existing_gap_) {
|
||||
@@ -1138,7 +1148,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal()
|
||||
// If we merged two gaps together, restore the second one now
|
||||
if (existing_merged_gap_) {
|
||||
original_gap_length -= existing_merged_gap_->length();
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(existing_merged_gap_);
|
||||
existing_merged_gap_->setParent(track_->parent());
|
||||
track_->InsertBlockAfter(existing_merged_gap_, existing_gap_);
|
||||
existing_merged_gap_ = nullptr;
|
||||
}
|
||||
@@ -1162,7 +1172,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal()
|
||||
|
||||
// However, we may have removed an unnecessary gap that preceded it
|
||||
if (existing_merged_gap_) {
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(existing_merged_gap_);
|
||||
existing_merged_gap_->setParent(track_->parent());
|
||||
track_->AppendBlock(existing_merged_gap_);
|
||||
existing_merged_gap_ = nullptr;
|
||||
}
|
||||
@@ -1174,7 +1184,7 @@ void TrackReplaceBlockWithGapCommand::undo_internal()
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input(), track_->block_input());
|
||||
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input());
|
||||
}
|
||||
|
||||
TrackSlideCommand::TrackSlideCommand(TrackOutput* track, const QList<Block*>& moving_blocks, Block *in_adjacent, Block *out_adjacent, const rational& movement, QUndoCommand* parent) :
|
||||
@@ -1218,12 +1228,12 @@ void TrackSlideCommand::slide_internal(bool undo)
|
||||
if (we_created_in_adjacent_) {
|
||||
// This is a gap we made, we can just delete it entirely
|
||||
track_->RippleRemoveBlock(in_adjacent_);
|
||||
TakeNodeFromParentGraph(in_adjacent_, &memory_manager_);
|
||||
in_adjacent_->setParent(&memory_manager_);
|
||||
we_created_in_adjacent_ = false;
|
||||
in_adjacent_ = nullptr;
|
||||
} else if (in_adjacent_->parent() == &memory_manager_) {
|
||||
// This is a gap we removed, we can re-insert it now
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(in_adjacent_);
|
||||
in_adjacent_->setParent(track_->parent());
|
||||
track_->InsertBlockBefore(in_adjacent_, blocks_.first());
|
||||
} else {
|
||||
// We must have just resized this block
|
||||
@@ -1233,7 +1243,7 @@ void TrackSlideCommand::slide_internal(bool undo)
|
||||
if (we_created_out_adjacent_) {
|
||||
// This is a gap we made, we can just delete it entirely
|
||||
track_->RippleRemoveBlock(out_adjacent_);
|
||||
TakeNodeFromParentGraph(out_adjacent_, &memory_manager_);
|
||||
out_adjacent_->setParent(&memory_manager_);
|
||||
we_created_out_adjacent_ = false;
|
||||
out_adjacent_ = nullptr;
|
||||
} else if (out_adjacent_) {
|
||||
@@ -1241,7 +1251,7 @@ void TrackSlideCommand::slide_internal(bool undo)
|
||||
// to check here
|
||||
if (out_adjacent_->parent() == &memory_manager_) {
|
||||
// This is a gap we removed, we can re-insert it now
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(out_adjacent_);
|
||||
out_adjacent_->setParent(track_->parent());
|
||||
track_->InsertBlockAfter(out_adjacent_, blocks_.last());
|
||||
} else {
|
||||
// We must have just resized this block
|
||||
@@ -1257,14 +1267,14 @@ void TrackSlideCommand::slide_internal(bool undo)
|
||||
// be created
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length_and_media_out(movement_);
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(gap);
|
||||
gap->setParent(track_->parent());
|
||||
track_->InsertBlockBefore(gap, blocks_.first());
|
||||
we_created_in_adjacent_ = true;
|
||||
in_adjacent_ = gap;
|
||||
} else if (-movement_ == in_adjacent_->length()) {
|
||||
// Remove in adjacent entirely
|
||||
track_->RippleRemoveBlock(in_adjacent_);
|
||||
TakeNodeFromParentGraph(in_adjacent_, &memory_manager_);
|
||||
in_adjacent_->setParent(&memory_manager_);
|
||||
} else {
|
||||
// Resize in adjacent
|
||||
in_adjacent_->set_length_and_media_out(in_adjacent_->length() + movement_);
|
||||
@@ -1276,7 +1286,7 @@ void TrackSlideCommand::slide_internal(bool undo)
|
||||
if (blocks_.last()->next()) {
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length_and_media_out(-movement_);
|
||||
static_cast<NodeGraph*>(track_->parent())->AddNode(gap);
|
||||
gap->setParent(track_->parent());
|
||||
track_->InsertBlockAfter(gap, blocks_.last());
|
||||
we_created_out_adjacent_ = true;
|
||||
out_adjacent_ = gap;
|
||||
@@ -1284,7 +1294,7 @@ void TrackSlideCommand::slide_internal(bool undo)
|
||||
} else if (movement_ == out_adjacent_->length()) {
|
||||
// Remove out adjacent entirely
|
||||
track_->RippleRemoveBlock(out_adjacent_);
|
||||
TakeNodeFromParentGraph(out_adjacent_, &memory_manager_);
|
||||
out_adjacent_->setParent(&memory_manager_);
|
||||
} else {
|
||||
// Resize out adjacent
|
||||
out_adjacent_->set_length_and_media_in(out_adjacent_->length() - movement_);
|
||||
@@ -1298,7 +1308,7 @@ void TrackSlideCommand::slide_internal(bool undo)
|
||||
invalidate_range.set_range(qMin(invalidate_range.in(), blocks_.first()->in()),
|
||||
qMax(invalidate_range.out(), blocks_.last()->out()));
|
||||
|
||||
track_->InvalidateCache(invalidate_range, track_->block_input(), track_->block_input());
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input());
|
||||
}
|
||||
|
||||
TrackListRippleRemoveAreaCommand::TrackListRippleRemoveAreaCommand(TrackList *list, rational in, rational out, QUndoCommand *parent) :
|
||||
@@ -1465,13 +1475,13 @@ void TrackListRippleToolCommand::redo_internal()
|
||||
// Assume the Block was a Gap and it was reduced to zero length, remove it here
|
||||
working_data_[i].removed_gap_after = b->previous();
|
||||
info.track->RippleRemoveBlock(b);
|
||||
TakeNodeFromParentGraph(b, &memory_manager_);
|
||||
b->setParent(&memory_manager_);
|
||||
}
|
||||
} else if (info.new_length > 0) {
|
||||
// This is a gap we are creating
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length_and_media_out(info.new_length);
|
||||
static_cast<NodeGraph*>(info.ref_block->parent())->AddNode(gap);
|
||||
gap->setParent(info.ref_block->parent());
|
||||
working_data_[i].created_gap = gap;
|
||||
|
||||
info.track->InsertBlockAfter(gap, info.ref_block);
|
||||
@@ -1508,7 +1518,6 @@ void TrackListRippleToolCommand::redo_internal()
|
||||
// FIXME: Untested, is this desirable behavior?
|
||||
if (earliest_pt < new_latest_pt) {
|
||||
info.track->InvalidateCache(TimeRange(earliest_pt, new_latest_pt),
|
||||
info.track->block_input(),
|
||||
info.track->block_input());
|
||||
}
|
||||
}
|
||||
@@ -1538,7 +1547,7 @@ void TrackListRippleToolCommand::undo_internal()
|
||||
// Assume the Block was a Gap and it was reduced to zero length, remove it here
|
||||
Block* previous_block = working_data_[i].removed_gap_after;
|
||||
|
||||
static_cast<NodeGraph*>(info.track->parent())->AddNode(b);
|
||||
b->setParent(info.track->parent());
|
||||
|
||||
if (previous_block) {
|
||||
info.track->InsertBlockAfter(b, previous_block);
|
||||
@@ -1551,7 +1560,7 @@ void TrackListRippleToolCommand::undo_internal()
|
||||
GapBlock* gap = working_data_.at(i).created_gap;
|
||||
|
||||
info.track->RippleRemoveBlock(gap);
|
||||
TakeNodeFromParentGraph(gap, &memory_manager_);
|
||||
gap->setParent(&memory_manager_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1627,7 +1636,7 @@ void TrackListInsertGaps::redo_internal()
|
||||
foreach (Block* block, blocks_to_append_gap_to) {
|
||||
GapBlock* gap = new GapBlock();
|
||||
gap->set_length_and_media_out(length_);
|
||||
static_cast<NodeGraph*>(block->parent())->AddNode(gap);
|
||||
gap->setParent(block->parent());
|
||||
TrackOutput::TrackFromBlock(block)->InsertBlockAfter(gap, block);
|
||||
gaps_added_.append(gap);
|
||||
}
|
||||
@@ -1657,7 +1666,7 @@ void TrackListInsertGaps::undo_internal()
|
||||
// Remove added gaps
|
||||
foreach (GapBlock* gap, gaps_added_) {
|
||||
TrackOutput::TrackFromBlock(gap)->RippleRemoveBlock(gap);
|
||||
TakeNodeFromParentGraph(gap, &memory_manager_);
|
||||
gap->setParent(&memory_manager_);
|
||||
}
|
||||
gaps_added_.clear();
|
||||
|
||||
@@ -1712,18 +1721,18 @@ void TransitionRemoveCommand::redo_internal()
|
||||
}
|
||||
|
||||
if (in_block_) {
|
||||
NodeParam::DisconnectEdge(in_block_->output(), block_->in_block_input());
|
||||
Node::DisconnectEdge(in_block_, block_->in_block_input());
|
||||
}
|
||||
|
||||
if (out_block_) {
|
||||
NodeParam::DisconnectEdge(out_block_->output(), block_->out_block_input());
|
||||
Node::DisconnectEdge(out_block_, block_->out_block_input());
|
||||
}
|
||||
|
||||
track_->RippleRemoveBlock(block_);
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->InvalidateCache(invalidate_range, track_->block_input(), track_->block_input());
|
||||
track_->Node::InvalidateCache(invalidate_range, track_->block_input());
|
||||
}
|
||||
|
||||
void TransitionRemoveCommand::undo_internal()
|
||||
@@ -1737,11 +1746,11 @@ void TransitionRemoveCommand::undo_internal()
|
||||
}
|
||||
|
||||
if (in_block_) {
|
||||
NodeParam::ConnectEdge(in_block_->output(), block_->in_block_input());
|
||||
Node::ConnectEdge(in_block_, block_->in_block_input());
|
||||
}
|
||||
|
||||
if (out_block_) {
|
||||
NodeParam::ConnectEdge(out_block_->output(), block_->out_block_input());
|
||||
Node::ConnectEdge(out_block_, block_->out_block_input());
|
||||
}
|
||||
|
||||
// These if statements must be separated because in_offset and out_offset report different things
|
||||
@@ -1757,7 +1766,7 @@ void TransitionRemoveCommand::undo_internal()
|
||||
|
||||
track_->EndOperation();
|
||||
|
||||
track_->InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input(), track_->block_input());
|
||||
track_->Node::InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input());
|
||||
}
|
||||
|
||||
TimelineSetSelectionsCommand::TimelineSetSelectionsCommand(TimelineWidget *timeline, const TimelineWidgetSelections &now, const TimelineWidgetSelections &old, QUndoCommand *parent) :
|
||||
|
||||
@@ -208,7 +208,7 @@ protected:
|
||||
|
||||
QObject memory_manager_;
|
||||
|
||||
QList<UndoCommand*> remove_block_commands_;
|
||||
QVector<QUndoCommand*> remove_block_commands_;
|
||||
|
||||
};
|
||||
|
||||
@@ -312,7 +312,8 @@ private:
|
||||
int track_index_;
|
||||
bool append_;
|
||||
GapBlock* gap_;
|
||||
int added_track_count_;
|
||||
QVector<TrackOutput*> added_tracks_;
|
||||
|
||||
};
|
||||
|
||||
class BlockSplitCommand : public UndoCommand {
|
||||
|
||||
@@ -45,22 +45,22 @@ void TimeTargetObject::SetPathIndex(int index)
|
||||
path_index_ = index;
|
||||
}
|
||||
|
||||
rational TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const rational &r, NodeParam::Type direction) const
|
||||
rational TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const rational &r, bool input_direction) const
|
||||
{
|
||||
if (!from || !to) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return GetAdjustedTime(from, to, TimeRange(r, r), direction).in();
|
||||
return GetAdjustedTime(from, to, TimeRange(r, r), input_direction).in();
|
||||
}
|
||||
|
||||
TimeRange TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const TimeRange &r, NodeParam::Type direction) const
|
||||
TimeRange TimeTargetObject::GetAdjustedTime(Node* from, Node* to, const TimeRange &r, bool input_direction) const
|
||||
{
|
||||
if (!from || !to) {
|
||||
return r;
|
||||
}
|
||||
|
||||
QVector<TimeRange> adjusted = from->TransformTimeTo(r, to, direction);
|
||||
QVector<TimeRange> adjusted = from->TransformTimeTo(r, to, input_direction);
|
||||
|
||||
if (adjusted.isEmpty()) {
|
||||
return r;
|
||||
|
||||
@@ -35,8 +35,8 @@ public:
|
||||
|
||||
void SetPathIndex(int index);
|
||||
|
||||
rational GetAdjustedTime(Node* from, Node* to, const rational& r, NodeParam::Type direction) const;
|
||||
TimeRange GetAdjustedTime(Node* from, Node* to, const TimeRange& r, NodeParam::Type direction) const;
|
||||
rational GetAdjustedTime(Node* from, Node* to, const rational& r, bool input_direction) const;
|
||||
TimeRange GetAdjustedTime(Node* from, Node* to, const TimeRange& r, bool input_direction) const;
|
||||
|
||||
//int GetNumberOfPathAdjustments(Node* from, NodeParam::Type direction) const;
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) :
|
||||
footage_(nullptr)
|
||||
{
|
||||
video_node_ = new MediaInput();
|
||||
sequence_.AddNode(video_node_);
|
||||
video_node_->setParent(&sequence_);
|
||||
|
||||
audio_node_ = new MediaInput();
|
||||
sequence_.AddNode(audio_node_);
|
||||
audio_node_->setParent(&sequence_);
|
||||
|
||||
connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag);
|
||||
|
||||
@@ -60,8 +60,8 @@ void FootageViewerWidget::SetFootage(Footage *footage)
|
||||
video_node_->SetStream(nullptr);
|
||||
audio_node_->SetStream(nullptr);
|
||||
|
||||
NodeParam::DisconnectEdge(video_node_->output(), sequence_.viewer_output()->texture_input());
|
||||
NodeParam::DisconnectEdge(audio_node_->output(), sequence_.viewer_output()->samples_input());
|
||||
Node::DisconnectEdge(video_node_, sequence_.viewer_output()->texture_input());
|
||||
Node::DisconnectEdge(audio_node_, sequence_.viewer_output()->samples_input());
|
||||
}
|
||||
|
||||
footage_ = footage;
|
||||
@@ -98,12 +98,12 @@ void FootageViewerWidget::SetFootage(Footage *footage)
|
||||
|
||||
if (video_stream) {
|
||||
video_node_->SetStream(video_stream);
|
||||
NodeParam::ConnectEdge(video_node_->output(), sequence_.viewer_output()->texture_input());
|
||||
Node::ConnectEdge(video_node_, sequence_.viewer_output()->texture_input());
|
||||
}
|
||||
|
||||
if (audio_stream) {
|
||||
audio_node_->SetStream(audio_stream);
|
||||
NodeParam::ConnectEdge(audio_node_->output(), sequence_.viewer_output()->samples_input());
|
||||
Node::ConnectEdge(audio_node_, sequence_.viewer_output()->samples_input());
|
||||
}
|
||||
|
||||
ConnectViewerNode(sequence_.viewer_output(), footage_->project()->color_manager());
|
||||
|
||||
@@ -178,7 +178,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
connect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters);
|
||||
connect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange);
|
||||
connect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange);
|
||||
connect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack);
|
||||
connect(n->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack);
|
||||
connect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack);
|
||||
|
||||
InterlacingChangedSlot(n->video_params().interlacing());
|
||||
|
||||
@@ -232,7 +233,8 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
disconnect(n, &ViewerOutput::AudioParamsChanged, this, &ViewerWidget::UpdateRendererAudioParameters);
|
||||
disconnect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedVideoRange);
|
||||
disconnect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange);
|
||||
disconnect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack);
|
||||
disconnect(n->texture_input(), &NodeInput::InputConnected, this, &ViewerWidget::UpdateStack);
|
||||
disconnect(n->texture_input(), &NodeInput::InputDisconnected, this, &ViewerWidget::UpdateStack);
|
||||
|
||||
ruler()->SetPlaybackCache(nullptr);
|
||||
|
||||
@@ -396,8 +398,8 @@ void ViewerWidget::DecodeCachedImage(RenderTicketPtr ticket, const QString &fn,
|
||||
bool ViewerWidget::ShouldForceWaveform() const
|
||||
{
|
||||
return GetConnectedNode()
|
||||
&& !GetConnectedNode()->texture_input()->is_connected()
|
||||
&& GetConnectedNode()->samples_input()->is_connected();
|
||||
&& !GetConnectedNode()->texture_input()->IsConnected()
|
||||
&& GetConnectedNode()->samples_input()->IsConnected();
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
|
||||
@@ -280,8 +280,8 @@ void ViewerDisplayWidget::OnPaint()
|
||||
}
|
||||
|
||||
ShaderJob job;
|
||||
job.InsertValue(QStringLiteral("resolution_in"), ShaderValue(QVector2D(texture_to_draw->width(), texture_to_draw->height()), NodeParam::kVec2));
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), ShaderValue(QVariant::fromValue(texture_to_draw), NodeParam::kTexture));
|
||||
job.InsertValue(QStringLiteral("resolution_in"), NodeValue(NodeValue::kVec2, QVector2D(texture_to_draw->width(), texture_to_draw->height())));
|
||||
job.InsertValue(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture_to_draw)));
|
||||
|
||||
renderer()->BlitToTexture(deinterlace_shader_, job, deinterlace_texture_.get());
|
||||
|
||||
@@ -375,7 +375,7 @@ QPointF ViewerDisplayWidget::GetTexturePosition(const double &x, const double &y
|
||||
|
||||
rational ViewerDisplayWidget::GetGizmoTime()
|
||||
{
|
||||
return GetAdjustedTime(GetTimeTarget(), gizmos_, time_, NodeParam::kInput);
|
||||
return GetAdjustedTime(GetTimeTarget(), gizmos_, time_, true);
|
||||
}
|
||||
|
||||
bool ViewerDisplayWidget::IsHandDrag(QMouseEvent *event) const
|
||||
|
||||
Reference in New Issue
Block a user