implemented the ability to modify bezier points in the curve editor
This commit is contained in:
+1
-2
@@ -479,8 +479,7 @@ void NodeInput::CopyValues(NodeInput *source, NodeInput *dest, bool include_conn
|
||||
// Copy keyframes
|
||||
dest->keyframes_.clear();
|
||||
foreach (NodeKeyframePtr key, source->keyframes_) {
|
||||
NodeKeyframePtr copy = std::make_shared<NodeKeyframe>(key->time(), key->value(), key->type());
|
||||
dest->keyframes_.append(copy);
|
||||
dest->keyframes_.append(key->copy());
|
||||
}
|
||||
|
||||
// Copy keyframing state
|
||||
|
||||
+65
-8
@@ -20,20 +20,28 @@
|
||||
|
||||
#include "keyframe.h"
|
||||
|
||||
NodeKeyframe::NodeKeyframe() :
|
||||
time_(0),
|
||||
value_(0),
|
||||
type_(kLinear)
|
||||
{
|
||||
}
|
||||
|
||||
NodeKeyframe::NodeKeyframe(const rational &time, const QVariant &value, const NodeKeyframe::Type &type) :
|
||||
time_(time),
|
||||
value_(value),
|
||||
type_(type)
|
||||
type_(type),
|
||||
bezier_control_in_(QPointF(-1.0, 0.0)),
|
||||
bezier_control_out_(QPointF(1.0, 0.0))
|
||||
{
|
||||
}
|
||||
|
||||
NodeKeyframePtr NodeKeyframe::Create(const rational &time, const QVariant &value, const NodeKeyframe::Type &type)
|
||||
{
|
||||
return std::make_shared<NodeKeyframe>(time, value, type);
|
||||
}
|
||||
|
||||
NodeKeyframePtr NodeKeyframe::copy() const
|
||||
{
|
||||
NodeKeyframePtr copy = std::make_shared<NodeKeyframe>(time_, value_, type_);
|
||||
copy->bezier_control_in_ = bezier_control_in_;
|
||||
copy->bezier_control_out_ = bezier_control_out_;
|
||||
return copy;
|
||||
}
|
||||
|
||||
const rational &NodeKeyframe::time() const
|
||||
{
|
||||
return time_;
|
||||
@@ -66,3 +74,52 @@ void NodeKeyframe::set_type(const NodeKeyframe::Type &type)
|
||||
type_ = type;
|
||||
emit TypeChanged(type_);
|
||||
}
|
||||
|
||||
const QPointF &NodeKeyframe::bezier_control_in() const
|
||||
{
|
||||
return bezier_control_in_;
|
||||
}
|
||||
|
||||
void NodeKeyframe::set_bezier_control_in(const QPointF &control)
|
||||
{
|
||||
bezier_control_in_ = control;
|
||||
emit BezierControlInChanged(bezier_control_in_);
|
||||
}
|
||||
|
||||
const QPointF &NodeKeyframe::bezier_control_out() const
|
||||
{
|
||||
return bezier_control_out_;
|
||||
}
|
||||
|
||||
void NodeKeyframe::set_bezier_control_out(const QPointF &control)
|
||||
{
|
||||
bezier_control_out_ = control;
|
||||
emit BezierControlOutChanged(bezier_control_out_);
|
||||
}
|
||||
|
||||
const QPointF &NodeKeyframe::bezier_control(NodeKeyframe::BezierType type) const
|
||||
{
|
||||
if (type == kInHandle) {
|
||||
return bezier_control_in();
|
||||
} else {
|
||||
return bezier_control_out();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeKeyframe::set_bezier_control(NodeKeyframe::BezierType type, const QPointF &control)
|
||||
{
|
||||
if (type == kInHandle) {
|
||||
set_bezier_control_in(control);
|
||||
} else {
|
||||
set_bezier_control_out(control);
|
||||
}
|
||||
}
|
||||
|
||||
NodeKeyframe::BezierType NodeKeyframe::get_opposing_bezier_type(NodeKeyframe::BezierType type)
|
||||
{
|
||||
if (type == kInHandle) {
|
||||
return kOutHandle;
|
||||
} else {
|
||||
return kInHandle;
|
||||
}
|
||||
}
|
||||
|
||||
+45
-5
@@ -36,7 +36,6 @@ class NodeKeyframe : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Methods of interpolation to use with this keyframe
|
||||
*/
|
||||
@@ -46,18 +45,25 @@ public:
|
||||
kBezier
|
||||
};
|
||||
|
||||
static const Type kDefaultType = kLinear;
|
||||
|
||||
/**
|
||||
* @brief NodeKeyframe Constructor
|
||||
* @brief The two types of bezier handles that are available on bezier keyframes
|
||||
*/
|
||||
NodeKeyframe();
|
||||
enum BezierType {
|
||||
kInHandle,
|
||||
kOutHandle
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief NodeKeyframe Constructor
|
||||
*/
|
||||
NodeKeyframe(const rational& time, const QVariant& value, const Type& type);
|
||||
|
||||
static const Type kDefaultType = kLinear;
|
||||
|
||||
static NodeKeyframePtr Create(const rational& time, const QVariant& value, const Type& type);
|
||||
|
||||
NodeKeyframePtr copy() const;
|
||||
|
||||
/**
|
||||
* @brief The time this keyframe is set at
|
||||
*/
|
||||
@@ -76,6 +82,26 @@ public:
|
||||
const Type& type() const;
|
||||
void set_type(const Type& type);
|
||||
|
||||
/**
|
||||
* @brief For bezier interpolation, the control point leading into this keyframe
|
||||
*/
|
||||
const QPointF &bezier_control_in() const;
|
||||
void set_bezier_control_in(const QPointF& control);
|
||||
|
||||
/**
|
||||
* @brief For bezier interpolation, the control point leading out of this keyframe
|
||||
*/
|
||||
const QPointF& bezier_control_out() const;
|
||||
void set_bezier_control_out(const QPointF& control);
|
||||
|
||||
/**
|
||||
* @brief Convenience functions for retrieving/setting bezier handle information with a BezierType
|
||||
*/
|
||||
const QPointF& bezier_control(BezierType type) const;
|
||||
void set_bezier_control(BezierType type, const QPointF& control);
|
||||
|
||||
static BezierType get_opposing_bezier_type(BezierType type);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when this keyframe's time is changed
|
||||
@@ -92,12 +118,26 @@ signals:
|
||||
*/
|
||||
void TypeChanged(const Type& type);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when this keyframe's bezier in control point is changed
|
||||
*/
|
||||
void BezierControlInChanged(const QPointF& d);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when this keyframe's bezier out control point is changed
|
||||
*/
|
||||
void BezierControlOutChanged(const QPointF& d);
|
||||
|
||||
private:
|
||||
rational time_;
|
||||
|
||||
QVariant value_;
|
||||
|
||||
Type type_;
|
||||
|
||||
QPointF bezier_control_in_;
|
||||
|
||||
QPointF bezier_control_out_;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(NodeKeyframe::Type)
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
widget/curvewidget/beziercontrolpointitem.h
|
||||
widget/curvewidget/beziercontrolpointitem.cpp
|
||||
widget/curvewidget/curveview.h
|
||||
widget/curvewidget/curveview.cpp
|
||||
widget/curvewidget/curvewidget.h
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "beziercontrolpointitem.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/qtversionabstraction.h"
|
||||
|
||||
BezierControlPointItem::BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem *parent) :
|
||||
QGraphicsRectItem(parent),
|
||||
key_(key),
|
||||
mode_(mode),
|
||||
x_scale_(1.0),
|
||||
y_scale_(1.0)
|
||||
{
|
||||
setFlag(QGraphicsItem::ItemIsMovable);
|
||||
|
||||
connect(key.get(), &NodeKeyframe::TimeChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
|
||||
if (mode_ == NodeKeyframe::kInHandle) {
|
||||
connect(key.get(), &NodeKeyframe::BezierControlInChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
} else {
|
||||
connect(key.get(), &NodeKeyframe::BezierControlOutChanged, this, &BezierControlPointItem::UpdatePos);
|
||||
}
|
||||
|
||||
|
||||
int control_point_size = QFontMetricsWidth(qApp->fontMetrics(), "o");
|
||||
int half_sz = control_point_size / 2;
|
||||
setRect(-half_sz, -half_sz, control_point_size, control_point_size);
|
||||
}
|
||||
|
||||
void BezierControlPointItem::SetXScale(double scale)
|
||||
{
|
||||
x_scale_ = scale;
|
||||
UpdatePos();
|
||||
}
|
||||
|
||||
void BezierControlPointItem::SetYScale(double scale)
|
||||
{
|
||||
y_scale_ = scale;
|
||||
UpdatePos();
|
||||
}
|
||||
|
||||
NodeKeyframePtr BezierControlPointItem::key() const
|
||||
{
|
||||
return key_;
|
||||
}
|
||||
|
||||
const NodeKeyframe::BezierType &BezierControlPointItem::mode() const
|
||||
{
|
||||
return mode_;
|
||||
}
|
||||
|
||||
QPointF BezierControlPointItem::GetCorrespondingKeyframeHandle() const
|
||||
{
|
||||
return key_->bezier_control(mode_);
|
||||
}
|
||||
|
||||
void BezierControlPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
if (option->state & QStyle::State_Selected) {
|
||||
painter->setPen(widget->palette().highlight().color());
|
||||
} else {
|
||||
painter->setPen(widget->palette().text().color());
|
||||
}
|
||||
|
||||
painter->drawEllipse(rect());
|
||||
}
|
||||
|
||||
void BezierControlPointItem::UpdatePos()
|
||||
{
|
||||
QPointF handle_offset = GetCorrespondingKeyframeHandle();
|
||||
|
||||
// Scale handle offset
|
||||
handle_offset.setX(handle_offset.x() * x_scale_);
|
||||
handle_offset.setY(handle_offset.y() * y_scale_);
|
||||
|
||||
setPos(handle_offset - rect().center());
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef BEZIERCONTROLPOINTITEM_H
|
||||
#define BEZIERCONTROLPOINTITEM_H
|
||||
|
||||
#include <QGraphicsRectItem>
|
||||
|
||||
#include "node/keyframe.h"
|
||||
|
||||
class BezierControlPointItem : public QObject, public QGraphicsRectItem
|
||||
{
|
||||
public:
|
||||
BezierControlPointItem(NodeKeyframePtr key, NodeKeyframe::BezierType mode, QGraphicsItem* parent = nullptr);
|
||||
|
||||
void SetXScale(double scale);
|
||||
|
||||
void SetYScale(double scale);
|
||||
|
||||
NodeKeyframePtr key() const;
|
||||
|
||||
const NodeKeyframe::BezierType& mode() const;
|
||||
|
||||
QPointF GetCorrespondingKeyframeHandle() const;
|
||||
|
||||
void SetCorrespondingKeyframeHandle(const QPointF& handle);
|
||||
|
||||
void SetOpposingKeyframeHandle(const QPointF& handle);
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
|
||||
NodeKeyframe::BezierType mode_;
|
||||
|
||||
double x_scale_;
|
||||
|
||||
double y_scale_;
|
||||
|
||||
private slots:
|
||||
void UpdatePos();
|
||||
|
||||
};
|
||||
|
||||
#endif // BEZIERCONTROLPOINTITEM_H
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "curveview.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QtMath>
|
||||
|
||||
#include "common/qtversionabstraction.h"
|
||||
@@ -15,6 +16,14 @@ CurveView::CurveView(QWidget *parent) :
|
||||
text_padding_ = QFontMetricsWidth(fontMetrics(), QStringLiteral("i"));
|
||||
|
||||
minimum_grid_space_ = QFontMetricsWidth(fontMetrics(), QStringLiteral("00000"));
|
||||
|
||||
connect(scene(), &QGraphicsScene::selectionChanged, this, &CurveView::SelectionChanged);
|
||||
}
|
||||
|
||||
CurveView::~CurveView()
|
||||
{
|
||||
// Quick way to avoid segfault when QGraphicsScene::selectionChanged is emitted after other memebers have been destroyed
|
||||
Clear();
|
||||
}
|
||||
|
||||
void CurveView::Clear()
|
||||
@@ -24,7 +33,6 @@ void CurveView::Clear()
|
||||
foreach (QGraphicsLineItem* line, lines_) {
|
||||
delete line;
|
||||
}
|
||||
|
||||
lines_.clear();
|
||||
}
|
||||
|
||||
@@ -40,6 +48,8 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
return;
|
||||
}
|
||||
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QVector<QLine> lines;
|
||||
|
||||
double x_interval = timebase().flipped().toDouble();
|
||||
@@ -89,7 +99,7 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
QVector<QLineF> keyframe_lines;
|
||||
|
||||
// Draw straight line leading to first keyframe
|
||||
QPointF first_key_pos = item_map().value(keys.first())->center_pos();
|
||||
QPointF first_key_pos = item_map().value(keys.first())->pos();
|
||||
keyframe_lines.append(QLineF(QPointF(scene_bottom_left.x(), first_key_pos.y()), first_key_pos));
|
||||
|
||||
// Draw lines between each keyframe
|
||||
@@ -102,30 +112,76 @@ void CurveView::drawBackground(QPainter *painter, const QRectF &rect)
|
||||
|
||||
if (before->type() == NodeKeyframe::kHold) {
|
||||
// Draw a hold keyframe (basically a right angle)
|
||||
keyframe_lines.append(QLineF(before_item->center_pos().x(),
|
||||
before_item->center_pos().y(),
|
||||
after_item->center_pos().x(),
|
||||
before_item->center_pos().y()));
|
||||
keyframe_lines.append(QLineF(after_item->center_pos().x(),
|
||||
before_item->center_pos().y(),
|
||||
after_item->center_pos().x(),
|
||||
after_item->center_pos().y()));
|
||||
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
|
||||
|
||||
// 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());
|
||||
|
||||
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);
|
||||
|
||||
} else {
|
||||
// Linear to linear
|
||||
keyframe_lines.append(QLineF(before_item->center_pos(), after_item->center_pos()));
|
||||
keyframe_lines.append(QLineF(before_item->pos(), after_item->pos()));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw straight line leading from end keyframe
|
||||
QPointF last_key_pos = item_map().value(keys.last())->center_pos();
|
||||
QPointF last_key_pos = item_map().value(keys.last())->pos();
|
||||
keyframe_lines.append(QLineF(last_key_pos, QPointF(scene_top_right.x(), last_key_pos.y())));
|
||||
|
||||
painter->drawLines(keyframe_lines);
|
||||
}
|
||||
|
||||
// Draw bezier control point lines
|
||||
if (!bezier_control_points_.isEmpty()) {
|
||||
painter->setPen(QPen(palette().text().color(), 1));
|
||||
|
||||
QVector<QLineF> bezier_lines;
|
||||
foreach (BezierControlPointItem* item, bezier_control_points_) {
|
||||
// All BezierControlPointItems should be children of a KeyframeViewItem
|
||||
KeyframeViewItem* par = static_cast<KeyframeViewItem*>(item->parentItem());
|
||||
|
||||
bezier_lines.append(QLineF(par->pos(), par->pos() + item->pos()));
|
||||
}
|
||||
painter->drawLines(bezier_lines);
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::KeyframeAboutToBeRemoved(NodeKeyframe *key)
|
||||
@@ -133,6 +189,15 @@ void CurveView::KeyframeAboutToBeRemoved(NodeKeyframe *key)
|
||||
disconnect(key, &NodeKeyframe::ValueChanged, this, &CurveView::KeyframeValueChanged);
|
||||
}
|
||||
|
||||
void CurveView::ScaleChangedEvent(double scale)
|
||||
{
|
||||
KeyframeViewBase::ScaleChangedEvent(scale);
|
||||
|
||||
foreach (BezierControlPointItem* item, bezier_control_points_) {
|
||||
item->SetXScale(scale);
|
||||
}
|
||||
}
|
||||
|
||||
QList<NodeKeyframe *> CurveView::GetKeyframesSortedByTime()
|
||||
{
|
||||
QList<NodeKeyframe *> sorted;
|
||||
@@ -169,6 +234,11 @@ void CurveView::SetItemYFromKeyframeValue(NodeKeyframe *key, KeyframeViewItem *i
|
||||
item->SetOverrideY(GetItemYFromKeyframeValue(key));
|
||||
}
|
||||
|
||||
QPointF CurveView::ScalePoint(const QPointF &point)
|
||||
{
|
||||
return QPointF(point.x() * scale_, point.y() * y_scale_);
|
||||
}
|
||||
|
||||
void CurveView::KeyframeValueChanged()
|
||||
{
|
||||
NodeKeyframe* key = static_cast<NodeKeyframe*>(sender());
|
||||
@@ -177,6 +247,37 @@ void CurveView::KeyframeValueChanged()
|
||||
SetItemYFromKeyframeValue(key, item);
|
||||
}
|
||||
|
||||
void CurveView::SelectionChanged()
|
||||
{
|
||||
// Clear current bezier handles
|
||||
foreach (BezierControlPointItem* item, bezier_control_points_) {
|
||||
delete item;
|
||||
}
|
||||
bezier_control_points_.clear();
|
||||
|
||||
QList<QGraphicsItem*> selected = scene()->selectedItems();
|
||||
|
||||
foreach (QGraphicsItem* item, selected) {
|
||||
KeyframeViewItem* this_item = static_cast<KeyframeViewItem*>(item);
|
||||
|
||||
BezierControlPointItem* bezier_in_pt = new BezierControlPointItem(this_item->key(), NodeKeyframe::kInHandle, item);
|
||||
bezier_in_pt->SetXScale(scale_);
|
||||
bezier_control_points_.append(bezier_in_pt);
|
||||
connect(bezier_in_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);
|
||||
|
||||
BezierControlPointItem* bezier_out_pt = new BezierControlPointItem(this_item->key(), NodeKeyframe::kOutHandle, item);
|
||||
bezier_out_pt->SetXScale(scale_);
|
||||
bezier_control_points_.append(bezier_out_pt);
|
||||
connect(bezier_out_pt, &QObject::destroyed, this, &CurveView::BezierControlPointDestroyed, Qt::DirectConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void CurveView::BezierControlPointDestroyed()
|
||||
{
|
||||
BezierControlPointItem* item = static_cast<BezierControlPointItem*>(sender());
|
||||
bezier_control_points_.removeOne(item);
|
||||
}
|
||||
|
||||
void CurveView::AddKeyframe(NodeKeyframePtr key)
|
||||
{
|
||||
KeyframeViewItem* item = AddKeyframeInternal(key);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef CURVEVIEW_H
|
||||
#define CURVEVIEW_H
|
||||
|
||||
#include "beziercontrolpointitem.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "widget/keyframeview/keyframeview.h"
|
||||
#include "widget/keyframeview/keyframeviewitem.h"
|
||||
@@ -10,6 +11,8 @@ class CurveView : public KeyframeViewBase
|
||||
public:
|
||||
CurveView(QWidget* parent = nullptr);
|
||||
|
||||
virtual ~CurveView() override;
|
||||
|
||||
virtual void Clear() override;
|
||||
|
||||
void SetYScale(const double& y_scale);
|
||||
@@ -22,6 +25,8 @@ protected:
|
||||
|
||||
virtual void KeyframeAboutToBeRemoved(NodeKeyframe *key) override;
|
||||
|
||||
virtual void ScaleChangedEvent(double scale) override;
|
||||
|
||||
private:
|
||||
QList<NodeKeyframe*> GetKeyframesSortedByTime();
|
||||
|
||||
@@ -29,6 +34,8 @@ private:
|
||||
|
||||
void SetItemYFromKeyframeValue(NodeKeyframe* key, KeyframeViewItem* item);
|
||||
|
||||
QPointF ScalePoint(const QPointF& point);
|
||||
|
||||
void AdjustLines();
|
||||
|
||||
int text_padding_;
|
||||
@@ -39,9 +46,15 @@ private:
|
||||
|
||||
QList<QGraphicsLineItem*> lines_;
|
||||
|
||||
QList<BezierControlPointItem*> bezier_control_points_;
|
||||
|
||||
private slots:
|
||||
void KeyframeValueChanged();
|
||||
|
||||
void SelectionChanged();
|
||||
|
||||
void BezierControlPointDestroyed();
|
||||
|
||||
};
|
||||
|
||||
#endif // CURVEVIEW_H
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "node/node.h"
|
||||
#include "widget/keyframeview/keyframeviewundo.h"
|
||||
#include "widget/nodeparamview/nodeparamviewkeyframecontrol.h"
|
||||
|
||||
CurveWidget::CurveWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
@@ -20,12 +19,9 @@ CurveWidget::CurveWidget(QWidget *parent) :
|
||||
|
||||
QHBoxLayout* top_controls = new QHBoxLayout();
|
||||
|
||||
NodeParamViewKeyframeControl* key_controls = new NodeParamViewKeyframeControl(false);
|
||||
key_controls->SetEnableButtonVisible(false);
|
||||
key_controls->SetPreviousButtonEnabled(false);
|
||||
key_controls->SetToggleButtonEnabled(false);
|
||||
key_controls->SetNextButtonEnabled(false);
|
||||
top_controls->addWidget(key_controls);
|
||||
key_control_ = new NodeParamViewKeyframeControl(false);
|
||||
connect(key_control_, &NodeParamViewKeyframeControl::RequestSetTime, this, &CurveWidget::KeyControlRequestedTimeChanged);
|
||||
top_controls->addWidget(key_control_);
|
||||
|
||||
top_controls->addStretch();
|
||||
|
||||
@@ -83,7 +79,7 @@ CurveWidget::CurveWidget(QWidget *parent) :
|
||||
|
||||
CurveWidget::~CurveWidget()
|
||||
{
|
||||
// Quick way to avoid segfault when QGraphicsScene::selectionChanged is emitted after the buttons have been destroyed
|
||||
// Quick way to avoid segfault when QGraphicsScene::selectionChanged is emitted after other memebers have been destroyed
|
||||
view_->Clear();
|
||||
}
|
||||
|
||||
@@ -105,6 +101,7 @@ void CurveWidget::SetInput(NodeInput *input)
|
||||
view_->Clear();
|
||||
|
||||
input_ = input;
|
||||
key_control_->SetInput(input_);
|
||||
|
||||
if (input_) {
|
||||
bridge_ = new NodeParamViewWidgetBridge(input_, this);
|
||||
@@ -195,6 +192,7 @@ void CurveWidget::UpdateBridgeTime(const int64_t ×tamp)
|
||||
|
||||
rational time = Timecode::timestamp_to_time(timestamp, view_->timebase());
|
||||
bridge_->SetTime(time);
|
||||
key_control_->SetTime(time);
|
||||
}
|
||||
|
||||
void CurveWidget::SelectionChanged()
|
||||
@@ -265,3 +263,12 @@ void CurveWidget::KeyframeTypeButtonTriggered(bool checked)
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
|
||||
void CurveWidget::KeyControlRequestedTimeChanged(const rational &time)
|
||||
{
|
||||
int64_t timestamp = Timecode::time_to_timestamp(time, view_->timebase());
|
||||
|
||||
SetTime(timestamp);
|
||||
|
||||
emit TimeChanged(timestamp);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "curveview.h"
|
||||
#include "node/input.h"
|
||||
#include "widget/nodeparamview/nodeparamviewkeyframecontrol.h"
|
||||
#include "widget/nodeparamview/nodeparamviewwidgetbridge.h"
|
||||
#include "widget/timeruler/timeruler.h"
|
||||
|
||||
@@ -60,6 +61,8 @@ private:
|
||||
|
||||
NodeParamViewWidgetBridge* bridge_;
|
||||
|
||||
NodeParamViewKeyframeControl* key_control_;
|
||||
|
||||
private slots:
|
||||
void UpdateBridgeTime(const int64_t& timestamp);
|
||||
|
||||
@@ -67,6 +70,8 @@ private slots:
|
||||
|
||||
void KeyframeTypeButtonTriggered(bool checked);
|
||||
|
||||
void KeyControlRequestedTimeChanged(const rational& time);
|
||||
|
||||
};
|
||||
|
||||
#endif // CURVEWIDGET_H
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#include "widget/nodeparamview/nodeparamviewundo.h"
|
||||
|
||||
KeyframeViewBase::KeyframeViewBase(QWidget *parent) :
|
||||
TimelineViewBase(parent)
|
||||
TimelineViewBase(parent),
|
||||
dragging_bezier_point_(nullptr)
|
||||
{
|
||||
setDragMode(RubberBandDrag);
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
@@ -62,16 +63,25 @@ void KeyframeViewBase::mousePressEvent(QMouseEvent *event)
|
||||
QGraphicsItem* item_under_cursor = itemAt(event->pos());
|
||||
|
||||
if (item_under_cursor) {
|
||||
QList<QGraphicsItem*> selected_items = scene()->selectedItems();
|
||||
|
||||
drag_start_ = event->pos();
|
||||
|
||||
selected_keys_.resize(selected_items.size());
|
||||
// Determine what type of item is under the cursor
|
||||
dragging_bezier_point_ = dynamic_cast<BezierControlPointItem*>(item_under_cursor);
|
||||
|
||||
for (int i=0;i<selected_items.size();i++) {
|
||||
KeyframeViewItem* key = static_cast<KeyframeViewItem*>(selected_items.at(i));
|
||||
if (dragging_bezier_point_) {
|
||||
dragging_bezier_point_start_ = dragging_bezier_point_->GetCorrespondingKeyframeHandle();
|
||||
dragging_bezier_point_opposing_start_ = dragging_bezier_point_->key()->bezier_control(NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode()));
|
||||
} else {
|
||||
QList<QGraphicsItem*> selected_items = scene()->selectedItems();
|
||||
|
||||
selected_keys_.replace(i, {key, key->x(), key->key()->time()});
|
||||
selected_keys_.resize(selected_items.size());
|
||||
|
||||
for (int i=0;i<selected_items.size();i++) {
|
||||
KeyframeViewItem* key = static_cast<KeyframeViewItem*>(selected_items.at(i));
|
||||
|
||||
selected_keys_.replace(i, {key, key->x(), key->key()->time()});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,13 +97,19 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
QGraphicsView::mouseMoveEvent(event);
|
||||
|
||||
if (active_tool_ == Tool::kPointer && !selected_keys_.isEmpty()) {
|
||||
int x_diff = event->pos().x() - drag_start_.x();
|
||||
double x_diff_scaled = static_cast<double>(x_diff) / scale_;
|
||||
if (active_tool_ == Tool::kPointer) {
|
||||
// Calculate cursor difference and scale it
|
||||
QPointF mouse_diff_scaled = GetScaledCursorPos(event->pos() - drag_start_);
|
||||
|
||||
foreach (const KeyframeItemAndTime& keypair, selected_keys_) {
|
||||
// FIXME: Find some way to do single frame updates as the NodeParamViewWidgetBridge does?
|
||||
keypair.key->key()->set_time(CalculateNewTimeFromScreen(keypair.time, x_diff_scaled));
|
||||
if (dragging_bezier_point_) {
|
||||
ProcessBezierDrag(mouse_diff_scaled,
|
||||
!(event->modifiers() & Qt::ControlModifier),
|
||||
false);
|
||||
} else if (!selected_keys_.isEmpty()) {
|
||||
foreach (const KeyframeItemAndTime& keypair, selected_keys_) {
|
||||
// FIXME: Find some way to do single frame updates as the NodeParamViewWidgetBridge does?
|
||||
keypair.key->key()->set_time(CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,29 +124,38 @@ void KeyframeViewBase::mouseReleaseEvent(QMouseEvent *event)
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
QGraphicsView::mouseReleaseEvent(event);
|
||||
|
||||
if (active_tool_ == Tool::kPointer && !selected_keys_.isEmpty()) {
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
if (active_tool_ == Tool::kPointer) {
|
||||
QPoint mouse_diff = event->pos() - drag_start_;
|
||||
QPointF mouse_diff_scaled = GetScaledCursorPos(mouse_diff);
|
||||
|
||||
// Calculate X movement and scaling to timeline time
|
||||
int x_diff = event->pos().x() - drag_start_.x();
|
||||
double x_diff_scaled = static_cast<double>(x_diff) / scale_;
|
||||
if (!mouse_diff.isNull()) {
|
||||
if (dragging_bezier_point_) {
|
||||
ProcessBezierDrag(mouse_diff_scaled,
|
||||
!(event->modifiers() & Qt::ControlModifier),
|
||||
true);
|
||||
|
||||
foreach (const KeyframeItemAndTime& keypair, selected_keys_) {
|
||||
KeyframeViewItem* item = keypair.key;
|
||||
dragging_bezier_point_ = nullptr;
|
||||
} else if (!selected_keys_.isEmpty()) {
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
// Calculate the new time for this keyframe
|
||||
rational new_time = CalculateNewTimeFromScreen(keypair.time, x_diff_scaled);
|
||||
foreach (const KeyframeItemAndTime& keypair, selected_keys_) {
|
||||
KeyframeViewItem* item = keypair.key;
|
||||
|
||||
// Commit movement
|
||||
new NodeParamSetKeyframeTimeCommand(item->key(),
|
||||
new_time,
|
||||
keypair.time,
|
||||
command);
|
||||
// Calculate the new time for this keyframe
|
||||
rational new_time = CalculateNewTimeFromScreen(keypair.time, mouse_diff_scaled.x());
|
||||
|
||||
// Commit movement
|
||||
new NodeParamSetKeyframeTimeCommand(item->key(),
|
||||
new_time,
|
||||
keypair.time,
|
||||
command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
|
||||
selected_keys_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
|
||||
selected_keys_.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,6 +183,77 @@ rational KeyframeViewBase::CalculateNewTimeFromScreen(const rational &old_time,
|
||||
return rational::fromDouble(old_time.toDouble() + cursor_diff);
|
||||
}
|
||||
|
||||
QPointF KeyframeViewBase::GenerateBezierControlPosition(const NodeKeyframe::BezierType mode, const QPointF &start_point, const QPointF &scaled_cursor_diff)
|
||||
{
|
||||
QPointF new_bezier_pos = start_point;
|
||||
|
||||
new_bezier_pos += scaled_cursor_diff;
|
||||
|
||||
// LIMIT bezier handles from overlapping each other
|
||||
if (mode == NodeKeyframe::kInHandle) {
|
||||
if (new_bezier_pos.x() > 0) {
|
||||
new_bezier_pos.setX(0);
|
||||
}
|
||||
} else {
|
||||
if (new_bezier_pos.x() < 0) {
|
||||
new_bezier_pos.setX(0);
|
||||
}
|
||||
}
|
||||
|
||||
return new_bezier_pos;
|
||||
}
|
||||
|
||||
void KeyframeViewBase::ProcessBezierDrag(const QPointF& mouse_diff_scaled, bool include_opposing, bool undoable)
|
||||
{
|
||||
QPointF new_bezier_pos = GenerateBezierControlPosition(dragging_bezier_point_->mode(),
|
||||
dragging_bezier_point_start_,
|
||||
mouse_diff_scaled);
|
||||
|
||||
// If the user is NOT holding control, we set the other handle to the exact negative of this handle
|
||||
QPointF new_opposing_pos;
|
||||
NodeKeyframe::BezierType opposing_type = NodeKeyframe::get_opposing_bezier_type(dragging_bezier_point_->mode());
|
||||
|
||||
if (include_opposing) {
|
||||
new_opposing_pos = GenerateBezierControlPosition(opposing_type,
|
||||
dragging_bezier_point_opposing_start_,
|
||||
-mouse_diff_scaled);
|
||||
} else {
|
||||
new_opposing_pos = dragging_bezier_point_opposing_start_;
|
||||
}
|
||||
|
||||
dragging_bezier_point_->key()->set_bezier_control(dragging_bezier_point_->mode(),
|
||||
new_bezier_pos);
|
||||
|
||||
dragging_bezier_point_->key()->set_bezier_control(opposing_type,
|
||||
new_opposing_pos);
|
||||
|
||||
if (undoable) {
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(),
|
||||
dragging_bezier_point_->mode(),
|
||||
new_bezier_pos,
|
||||
dragging_bezier_point_start_,
|
||||
command);
|
||||
|
||||
if (include_opposing) {
|
||||
new KeyframeSetBezierControlPoint(dragging_bezier_point_->key(),
|
||||
opposing_type,
|
||||
new_opposing_pos,
|
||||
dragging_bezier_point_opposing_start_,
|
||||
command);
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
}
|
||||
}
|
||||
|
||||
QPointF KeyframeViewBase::GetScaledCursorPos(const QPoint &cursor_pos)
|
||||
{
|
||||
return QPointF (static_cast<double>(cursor_pos.x()) / scale_,
|
||||
cursor_pos.y());;
|
||||
}
|
||||
|
||||
void KeyframeViewBase::ShowContextMenu()
|
||||
{
|
||||
Menu m;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "core.h"
|
||||
#include "keyframeviewitem.h"
|
||||
#include "node/keyframe.h"
|
||||
#include "widget/curvewidget/beziercontrolpointitem.h"
|
||||
#include "widget/timelinewidget/view/timelineviewbase.h"
|
||||
|
||||
class KeyframeViewBase : public TimelineViewBase
|
||||
@@ -33,6 +34,14 @@ protected:
|
||||
private:
|
||||
rational CalculateNewTimeFromScreen(const rational& old_time, double cursor_diff);
|
||||
|
||||
static QPointF GenerateBezierControlPosition(const NodeKeyframe::BezierType mode,
|
||||
const QPointF& start_point,
|
||||
const QPointF& scaled_cursor_diff);
|
||||
|
||||
void ProcessBezierDrag(const QPointF& mouse_diff_scaled, bool include_opposing, bool undoable);
|
||||
|
||||
QPointF GetScaledCursorPos(const QPoint& cursor_pos);
|
||||
|
||||
struct KeyframeItemAndTime {
|
||||
KeyframeViewItem* key;
|
||||
qreal item_x;
|
||||
@@ -45,6 +54,10 @@ private:
|
||||
|
||||
QPoint drag_start_;
|
||||
|
||||
BezierControlPointItem* dragging_bezier_point_;
|
||||
QPointF dragging_bezier_point_start_;
|
||||
QPointF dragging_bezier_point_opposing_start_;
|
||||
|
||||
QVector<KeyframeItemAndTime> selected_keys_;
|
||||
|
||||
private slots:
|
||||
|
||||
@@ -13,13 +13,14 @@ KeyframeViewItem::KeyframeViewItem(NodeKeyframePtr key, QGraphicsItem *parent) :
|
||||
scale_(1.0),
|
||||
vert_center_(0)
|
||||
{
|
||||
keyframe_size_ = QFontMetricsWidth(qApp->fontMetrics(), "Oi");
|
||||
setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
|
||||
connect(key.get(), &NodeKeyframe::TimeChanged, this, &KeyframeViewItem::UpdatePos);
|
||||
connect(key.get(), &NodeKeyframe::TypeChanged, this, &KeyframeViewItem::Redraw);
|
||||
|
||||
setRect(0, 0, keyframe_size_, keyframe_size_);
|
||||
int keyframe_size = QFontMetricsWidth(qApp->fontMetrics(), "Oi");
|
||||
int half_sz = keyframe_size/2;
|
||||
setRect(-half_sz, -half_sz, keyframe_size, keyframe_size);
|
||||
|
||||
UpdatePos();
|
||||
}
|
||||
@@ -41,11 +42,6 @@ NodeKeyframePtr KeyframeViewItem::key() const
|
||||
return key_;
|
||||
}
|
||||
|
||||
QPointF KeyframeViewItem::center_pos() const
|
||||
{
|
||||
return pos() + rect().center();
|
||||
}
|
||||
|
||||
void KeyframeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
painter->setPen(Qt::black);
|
||||
@@ -82,7 +78,7 @@ void KeyframeViewItem::UpdatePos()
|
||||
{
|
||||
double x_center = key_->time().toDouble() * scale_;
|
||||
|
||||
setPos(x_center - keyframe_size_/2, vert_center_ - keyframe_size_/2);
|
||||
setPos(x_center, vert_center_);
|
||||
}
|
||||
|
||||
void KeyframeViewItem::Redraw()
|
||||
|
||||
@@ -17,8 +17,6 @@ public:
|
||||
|
||||
NodeKeyframePtr key() const;
|
||||
|
||||
QPointF center_pos() const;
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
|
||||
|
||||
@@ -29,8 +27,6 @@ private:
|
||||
|
||||
qreal vert_center_;
|
||||
|
||||
int keyframe_size_;
|
||||
|
||||
private slots:
|
||||
void UpdatePos();
|
||||
|
||||
|
||||
@@ -17,3 +17,31 @@ void KeyframeSetTypeCommand::undo()
|
||||
{
|
||||
key_->set_type(old_type_);
|
||||
}
|
||||
|
||||
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
key_(key),
|
||||
mode_(mode),
|
||||
old_point_(key->bezier_control(mode_)),
|
||||
new_point_(point)
|
||||
{
|
||||
}
|
||||
|
||||
KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF &new_point, const QPointF &old_point, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
key_(key),
|
||||
mode_(mode),
|
||||
old_point_(old_point),
|
||||
new_point_(new_point)
|
||||
{
|
||||
}
|
||||
|
||||
void KeyframeSetBezierControlPoint::redo()
|
||||
{
|
||||
key_->set_bezier_control(mode_, new_point_);
|
||||
}
|
||||
|
||||
void KeyframeSetBezierControlPoint::undo()
|
||||
{
|
||||
key_->set_bezier_control(mode_, old_point_);
|
||||
}
|
||||
|
||||
@@ -21,4 +21,23 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class KeyframeSetBezierControlPoint : public QUndoCommand {
|
||||
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);
|
||||
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
NodeKeyframePtr key_;
|
||||
|
||||
NodeKeyframe::BezierType mode_;
|
||||
|
||||
QPointF old_point_;
|
||||
|
||||
QPointF new_point_;
|
||||
|
||||
};
|
||||
|
||||
#endif // KEYFRAMEVIEWUNDO_H
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <QCheckBox>
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
|
||||
#include "core.h"
|
||||
@@ -84,7 +83,7 @@ void NodeParamViewItem::SetTime(const rational &time)
|
||||
}
|
||||
|
||||
foreach (NodeParamViewKeyframeControl* key_control, key_control_list_) {
|
||||
UpdateKeyframeControl(key_control);
|
||||
key_control->SetTime(time_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,9 +120,6 @@ void NodeParamViewItem::InputAddedKeyframeInternal(NodeInput *input, NodeKeyfram
|
||||
// Find global position
|
||||
lbl_center = lbl->mapToGlobal(lbl_center);
|
||||
|
||||
// Update keyframe control widget
|
||||
UpdateKeyframeControl(KeyframeControlFromInput(input));
|
||||
|
||||
emit KeyframeAdded(keyframe, lbl_center.y());
|
||||
}
|
||||
|
||||
@@ -163,10 +159,7 @@ void NodeParamViewItem::SetupUI()
|
||||
NodeParamViewKeyframeControl* key_control = new NodeParamViewKeyframeControl();
|
||||
key_control->SetInput(input);
|
||||
content_layout_->addWidget(key_control, row_count, control_column);
|
||||
connect(key_control, &NodeParamViewKeyframeControl::KeyframeEnableChanged, this, &NodeParamViewItem::UserChangedKeyframeEnable);
|
||||
connect(key_control, &NodeParamViewKeyframeControl::GoToPreviousKey, this, &NodeParamViewItem::GoToPreviousKey);
|
||||
connect(key_control, &NodeParamViewKeyframeControl::GoToNextKey, this, &NodeParamViewItem::GoToNextKey);
|
||||
connect(key_control, &NodeParamViewKeyframeControl::KeyframeToggled, this, &NodeParamViewItem::UserToggledKeyframe);
|
||||
connect(key_control, &NodeParamViewKeyframeControl::RequestSetTime, this, &NodeParamViewItem::RequestSetTime);
|
||||
key_control_list_.append(key_control);
|
||||
|
||||
connect(input, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewItem::InputKeyframeEnableChanged);
|
||||
@@ -199,16 +192,6 @@ void NodeParamViewItem::Retranslate()
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItem::UpdateKeyframeControl(NodeParamViewKeyframeControl *key_control)
|
||||
{
|
||||
NodeInput* input = key_control->GetConnectedInput();
|
||||
|
||||
// Update UI based on time
|
||||
key_control->SetPreviousButtonEnabled(!input->keyframes().isEmpty() && time_ > input->keyframes().first()->time());
|
||||
key_control->SetNextButtonEnabled(!input->keyframes().isEmpty() && time_ < input->keyframes().last()->time());
|
||||
key_control->SetToggleButtonChecked(input->has_keyframe_at_time(time_));
|
||||
}
|
||||
|
||||
NodeParamViewKeyframeControl *NodeParamViewItem::KeyframeControlFromInput(NodeInput *input) const
|
||||
{
|
||||
foreach (NodeParamViewKeyframeControl* key_control, key_control_list_) {
|
||||
@@ -233,83 +216,6 @@ void NodeParamViewItem::SetExpanded(bool e)
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItem::UserChangedKeyframeEnable(bool e)
|
||||
{
|
||||
NodeParamViewKeyframeControl* control = static_cast<NodeParamViewKeyframeControl*>(sender());
|
||||
NodeInput* input = control->GetConnectedInput();
|
||||
|
||||
if (e == input->is_keyframing()) {
|
||||
// No-op
|
||||
return;
|
||||
}
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
if (e) {
|
||||
// Enable keyframing
|
||||
new NodeParamSetKeyframingCommand(input, true, command);
|
||||
|
||||
// NodeInputs already have one keyframe by default, we move it to the current time here
|
||||
NodeKeyframePtr key = std::make_shared<NodeKeyframe>(time_, input->get_standard_value(), NodeKeyframe::kDefaultType);
|
||||
new NodeParamInsertKeyframeCommand(input, key, command);
|
||||
} else {
|
||||
// Confirm the user wants to clear all keyframes
|
||||
if (QMessageBox::warning(this,
|
||||
tr("Warning"),
|
||||
tr("Are you sure you want to disable keyframing on this value? This will clear all existing keyframes."),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
|
||||
// Store value at this time, we'll set this as the persistent value later
|
||||
QVariant stored_val = input->get_value_at_time(time_);
|
||||
|
||||
// Delete all keyframes
|
||||
for (int i=input->keyframes().size()-1;i>=0;i--) {
|
||||
new NodeParamRemoveKeyframeCommand(input, input->keyframes().at(i), command);
|
||||
}
|
||||
|
||||
// Update standard value
|
||||
new NodeParamSetStandardValueCommand(input, stored_val, command);
|
||||
|
||||
// Disable keyframing
|
||||
new NodeParamSetKeyframingCommand(input, false, command);
|
||||
|
||||
} else {
|
||||
// Disable action has effectively been ignored
|
||||
control->SetKeyframeEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void NodeParamViewItem::UserToggledKeyframe(bool e)
|
||||
{
|
||||
NodeParamViewKeyframeControl* control = static_cast<NodeParamViewKeyframeControl*>(sender());
|
||||
NodeInput* input = control->GetConnectedInput();
|
||||
NodeKeyframePtr key = input->get_keyframe_at_time(time_);
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
if (e && !key) {
|
||||
// Add a keyframe here
|
||||
key = std::make_shared<NodeKeyframe>(time_,
|
||||
input->get_value_at_time(time_),
|
||||
input->get_best_keyframe_type_for_time(time_));
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input, key, command);
|
||||
} else if (!e && key) {
|
||||
// Remove a keyframe here
|
||||
new NodeParamRemoveKeyframeCommand(input, key, command);
|
||||
|
||||
// If this was the last keyframe, we'll set the standard value to the value at this time too
|
||||
if (input->keyframes().size() == 1) {
|
||||
new NodeParamSetStandardValueCommand(input, key->value(), command);
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void NodeParamViewItem::InputKeyframeEnableChanged(bool e)
|
||||
{
|
||||
NodeInput* input = static_cast<NodeInput*>(sender());
|
||||
@@ -333,38 +239,6 @@ void NodeParamViewItem::InputAddedKeyframe(NodeKeyframePtr key)
|
||||
InputAddedKeyframeInternal(input, key);
|
||||
}
|
||||
|
||||
void NodeParamViewItem::GoToPreviousKey()
|
||||
{
|
||||
NodeParamViewKeyframeControl* key_control = static_cast<NodeParamViewKeyframeControl*>(sender());
|
||||
NodeInput* input = key_control->GetConnectedInput();
|
||||
|
||||
for (int i=input->keyframes().size()-1;i>=0;i--) {
|
||||
// Find closest keyframe that is before this time
|
||||
const rational& this_key_time = input->keyframes().at(i)->time();
|
||||
|
||||
if (this_key_time < time_) {
|
||||
emit RequestSetTime(this_key_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItem::GoToNextKey()
|
||||
{
|
||||
NodeParamViewKeyframeControl* key_control = static_cast<NodeParamViewKeyframeControl*>(sender());
|
||||
NodeInput* input = key_control->GetConnectedInput();
|
||||
|
||||
for (int i=0;i<input->keyframes().size();i++) {
|
||||
// Find closest keyframe that is before this time
|
||||
const rational& this_key_time = input->keyframes().at(i)->time();
|
||||
|
||||
if (this_key_time > time_) {
|
||||
emit RequestSetTime(this_key_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewItem::LabelClicked()
|
||||
{
|
||||
ClickableLabel* lbl = static_cast<ClickableLabel*>(sender());
|
||||
|
||||
@@ -70,8 +70,6 @@ private:
|
||||
|
||||
void Retranslate();
|
||||
|
||||
void UpdateKeyframeControl(NodeParamViewKeyframeControl* key_control);
|
||||
|
||||
NodeParamViewKeyframeControl* KeyframeControlFromInput(NodeInput* input) const;
|
||||
|
||||
bool expanded_;
|
||||
@@ -101,18 +99,10 @@ private:
|
||||
private slots:
|
||||
void SetExpanded(bool e);
|
||||
|
||||
void UserChangedKeyframeEnable(bool e);
|
||||
|
||||
void UserToggledKeyframe(bool e);
|
||||
|
||||
void InputKeyframeEnableChanged(bool e);
|
||||
|
||||
void InputAddedKeyframe(NodeKeyframePtr key);
|
||||
|
||||
void GoToPreviousKey();
|
||||
|
||||
void GoToNextKey();
|
||||
|
||||
void LabelClicked();
|
||||
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "nodeparamviewkeyframecontrol.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "core.h"
|
||||
#include "nodeparamviewundo.h"
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWidget *parent) :
|
||||
@@ -37,9 +40,13 @@ NodeParamViewKeyframeControl::NodeParamViewKeyframeControl(bool right_align, QWi
|
||||
|
||||
connect(prev_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::GoToPreviousKey);
|
||||
connect(next_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::GoToNextKey);
|
||||
connect(toggle_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::KeyframeToggled);
|
||||
connect(toggle_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::ToggleKeyframe);
|
||||
connect(enable_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable);
|
||||
connect(enable_key_btn_, &QPushButton::toggled, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged);
|
||||
connect(enable_key_btn_, &QPushButton::clicked, this, &NodeParamViewKeyframeControl::KeyframeEnableChanged);
|
||||
|
||||
// Set defaults
|
||||
SetInput(nullptr);
|
||||
ShowButtonsFromKeyframeEnable(false);
|
||||
}
|
||||
|
||||
NodeInput *NodeParamViewKeyframeControl::GetConnectedInput() const
|
||||
@@ -47,60 +54,35 @@ NodeInput *NodeParamViewKeyframeControl::GetConnectedInput() const
|
||||
return input_;
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetPreviousButtonEnabled(bool enabled)
|
||||
{
|
||||
prev_key_btn_->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetNextButtonEnabled(bool enabled)
|
||||
{
|
||||
next_key_btn_->setEnabled(enabled);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetToggleButtonEnabled(bool enable)
|
||||
{
|
||||
toggle_key_btn_->setEnabled(enable);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetToggleButtonChecked(bool checked)
|
||||
{
|
||||
// Suppress KeyframeToggled() signal from this object
|
||||
blockSignals(true);
|
||||
|
||||
toggle_key_btn_->setChecked(checked);
|
||||
|
||||
blockSignals(false);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetEnableButtonVisible(bool visible)
|
||||
{
|
||||
enable_key_btn_->setVisible(visible);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetInput(NodeInput *input)
|
||||
{
|
||||
if (input_ != nullptr) {
|
||||
disconnect(input_, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewKeyframeControl::SetKeyframeEnabled);
|
||||
disconnect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked);
|
||||
disconnect(input_, &NodeInput::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState);
|
||||
disconnect(input_, &NodeInput::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState);
|
||||
}
|
||||
|
||||
input_ = input;
|
||||
SetButtonsEnabled(input_);
|
||||
|
||||
// Pick up keyframing value
|
||||
enable_key_btn_->setChecked(input_ && input_->is_keyframing());
|
||||
|
||||
// Update buttons
|
||||
UpdateState();
|
||||
|
||||
if (input_ != nullptr) {
|
||||
connect(input_, &NodeInput::KeyframeEnableChanged, this, &NodeParamViewKeyframeControl::SetKeyframeEnabled);
|
||||
|
||||
// Pick up keyframing value
|
||||
ShowButtonsFromKeyframeEnable(input_->is_keyframing());
|
||||
connect(input_, &NodeInput::KeyframeEnableChanged, enable_key_btn_, &QPushButton::setChecked);
|
||||
connect(input_, &NodeInput::KeyframeAdded, this, &NodeParamViewKeyframeControl::UpdateState);
|
||||
connect(input_, &NodeInput::KeyframeRemoved, this, &NodeParamViewKeyframeControl::UpdateState);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetKeyframeEnabled(bool e)
|
||||
void NodeParamViewKeyframeControl::SetTime(const rational &time)
|
||||
{
|
||||
// Suppress KeyframeEnableChanged() signal from this object
|
||||
blockSignals(true);
|
||||
time_ = time;
|
||||
|
||||
enable_key_btn_->setChecked(e);
|
||||
|
||||
blockSignals(false);
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
QPushButton *NodeParamViewKeyframeControl::CreateNewToolButton(const QIcon& icon) const
|
||||
@@ -112,9 +94,126 @@ QPushButton *NodeParamViewKeyframeControl::CreateNewToolButton(const QIcon& icon
|
||||
return btn;
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::SetButtonsEnabled(bool e)
|
||||
{
|
||||
prev_key_btn_->setEnabled(e);
|
||||
toggle_key_btn_->setEnabled(e);
|
||||
next_key_btn_->setEnabled(e);
|
||||
enable_key_btn_->setEnabled(e);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::ShowButtonsFromKeyframeEnable(bool e)
|
||||
{
|
||||
prev_key_btn_->setVisible(e);
|
||||
toggle_key_btn_->setVisible(e);
|
||||
next_key_btn_->setVisible(e);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::ToggleKeyframe(bool e)
|
||||
{
|
||||
NodeKeyframePtr key = input_->get_keyframe_at_time(time_);
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
if (e && !key) {
|
||||
// Add a keyframe here
|
||||
key = NodeKeyframe::Create(time_,
|
||||
input_->get_value_at_time(time_),
|
||||
input_->get_best_keyframe_type_for_time(time_));
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input_, key, command);
|
||||
} else if (!e && key) {
|
||||
// Remove a keyframe here
|
||||
new NodeParamRemoveKeyframeCommand(input_, key, command);
|
||||
|
||||
// If this was the last keyframe, we'll set the standard value to the value at this time too
|
||||
if (input_->keyframes().size() == 1) {
|
||||
new NodeParamSetStandardValueCommand(input_, key->value(), command);
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::UpdateState()
|
||||
{
|
||||
if (!input_) {
|
||||
return;
|
||||
}
|
||||
|
||||
prev_key_btn_->setEnabled(!input_->keyframes().isEmpty() && time_ > input_->keyframes().first()->time());
|
||||
next_key_btn_->setEnabled(!input_->keyframes().isEmpty() && time_ < input_->keyframes().last()->time());
|
||||
toggle_key_btn_->setChecked(input_->has_keyframe_at_time(time_));
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::GoToPreviousKey()
|
||||
{
|
||||
for (int i=input_->keyframes().size()-1;i>=0;i--) {
|
||||
// Find closest keyframe that is before this time
|
||||
const rational& this_key_time = input_->keyframes().at(i)->time();
|
||||
|
||||
if (this_key_time < time_) {
|
||||
emit RequestSetTime(this_key_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::GoToNextKey()
|
||||
{
|
||||
for (int i=0;i<input_->keyframes().size();i++) {
|
||||
// Find closest keyframe that is before this time
|
||||
const rational& this_key_time = input_->keyframes().at(i)->time();
|
||||
|
||||
if (this_key_time > time_) {
|
||||
emit RequestSetTime(this_key_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeParamViewKeyframeControl::KeyframeEnableChanged(bool e)
|
||||
{
|
||||
if (e == input_->is_keyframing()) {
|
||||
// No-op
|
||||
return;
|
||||
}
|
||||
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
|
||||
if (e) {
|
||||
// Enable keyframing
|
||||
new NodeParamSetKeyframingCommand(input_, true, command);
|
||||
|
||||
// NodeInputs already have one keyframe by default, we move it to the current time here
|
||||
NodeKeyframePtr key = NodeKeyframe::Create(time_, input_->get_standard_value(), NodeKeyframe::kDefaultType);
|
||||
new NodeParamInsertKeyframeCommand(input_, key, command);
|
||||
} else {
|
||||
// Confirm the user wants to clear all keyframes
|
||||
if (QMessageBox::warning(this,
|
||||
tr("Warning"),
|
||||
tr("Are you sure you want to disable keyframing on this value? This will clear all existing keyframes."),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
|
||||
// Store value at this time, we'll set this as the persistent value later
|
||||
QVariant stored_val = input_->get_value_at_time(time_);
|
||||
|
||||
// Delete all keyframes
|
||||
for (int i=input_->keyframes().size()-1;i>=0;i--) {
|
||||
new NodeParamRemoveKeyframeCommand(input_, input_->keyframes().at(i), command);
|
||||
}
|
||||
|
||||
// Update standard value
|
||||
new NodeParamSetStandardValueCommand(input_, stored_val, command);
|
||||
|
||||
// Disable keyframing
|
||||
new NodeParamSetKeyframingCommand(input_, false, command);
|
||||
|
||||
} else {
|
||||
// Disable action has effectively been ignored
|
||||
enable_key_btn_->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
Core::instance()->undo_stack()->pushIfHasChildren(command);
|
||||
}
|
||||
|
||||
@@ -14,28 +14,18 @@ public:
|
||||
|
||||
NodeInput* GetConnectedInput() const;
|
||||
|
||||
void SetPreviousButtonEnabled(bool enabled);
|
||||
void SetNextButtonEnabled(bool enabled);
|
||||
void SetToggleButtonEnabled(bool enable);
|
||||
void SetToggleButtonChecked(bool checked);
|
||||
void SetEnableButtonVisible(bool visible);
|
||||
|
||||
void SetInput(NodeInput* input);
|
||||
|
||||
public slots:
|
||||
void SetKeyframeEnabled(bool e);
|
||||
void SetTime(const rational& time);
|
||||
|
||||
signals:
|
||||
void KeyframeEnableChanged(bool);
|
||||
|
||||
void GoToPreviousKey();
|
||||
void GoToNextKey();
|
||||
|
||||
void KeyframeToggled(bool);
|
||||
void RequestSetTime(const rational& time);
|
||||
|
||||
private:
|
||||
QPushButton* CreateNewToolButton(const QIcon &icon) const;
|
||||
|
||||
void SetButtonsEnabled(bool e);
|
||||
|
||||
QPushButton* prev_key_btn_;
|
||||
QPushButton* toggle_key_btn_;
|
||||
QPushButton* next_key_btn_;
|
||||
@@ -43,8 +33,21 @@ private:
|
||||
|
||||
NodeInput* input_;
|
||||
|
||||
rational time_;
|
||||
|
||||
private slots:
|
||||
void ShowButtonsFromKeyframeEnable(bool e);
|
||||
|
||||
void ToggleKeyframe(bool e);
|
||||
|
||||
void UpdateState();
|
||||
|
||||
void GoToPreviousKey();
|
||||
|
||||
void GoToNextKey();
|
||||
|
||||
void KeyframeEnableChanged(bool e);
|
||||
|
||||
};
|
||||
|
||||
#endif // NODEPARAMVIEWKEYFRAMECONTROL_H
|
||||
|
||||
@@ -262,9 +262,9 @@ void NodeParamViewWidgetBridge::SetInputValue(const QVariant &value)
|
||||
new NodeParamSetKeyframeValueCommand(existing_key, value, command);
|
||||
} else {
|
||||
// No existing key, create a new one
|
||||
NodeKeyframePtr new_key = std::make_shared<NodeKeyframe>(time_,
|
||||
value,
|
||||
input_->get_best_keyframe_type_for_time(time_));
|
||||
NodeKeyframePtr new_key = NodeKeyframe::Create(time_,
|
||||
value,
|
||||
input_->get_best_keyframe_type_for_time(time_));
|
||||
|
||||
new NodeParamInsertKeyframeCommand(input_, new_key, command);
|
||||
}
|
||||
@@ -295,9 +295,9 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant
|
||||
drag_created_keyframe_ = !dragging_keyframe_;
|
||||
|
||||
if (drag_created_keyframe_) {
|
||||
dragging_keyframe_ = std::make_shared<NodeKeyframe>(time_,
|
||||
value,
|
||||
input_->get_best_keyframe_type_for_time(time_));
|
||||
dragging_keyframe_ = NodeKeyframe::Create(time_,
|
||||
value,
|
||||
input_->get_best_keyframe_type_for_time(time_));
|
||||
|
||||
input_->insert_keyframe(dragging_keyframe_);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "ui/style/style.h"
|
||||
#include "undo/undostack.h"
|
||||
#include "widget/menu/menushared.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
MainMenu::MainMenu(QMainWindow *parent) :
|
||||
QMenuBar(parent)
|
||||
@@ -207,7 +208,7 @@ MainMenu::MainMenu(QMainWindow *parent) :
|
||||
window_lock_layout_item_ = window_menu_->AddItem("lockpanels", PanelManager::instance(), SLOT(SetPanelsLocked(bool)));
|
||||
window_lock_layout_item_->setCheckable(true);
|
||||
window_menu_->addSeparator();
|
||||
window_reset_layout_item_ = window_menu_->AddItem("resetdefaultlayout", nullptr, nullptr);
|
||||
window_reset_layout_item_ = window_menu_->AddItem("resetdefaultlayout", Core::instance()->main_window(), SLOT(SetDefaultLayout()));
|
||||
|
||||
//
|
||||
// TOOLS MENU
|
||||
|
||||
@@ -48,6 +48,8 @@ public slots:
|
||||
void SetFullscreen(bool fullscreen);
|
||||
void ToggleMaximizedPanel();
|
||||
|
||||
void SetDefaultLayout();
|
||||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent* e) override;
|
||||
|
||||
@@ -65,9 +67,6 @@ private:
|
||||
TaskManagerPanel* task_man_panel_;
|
||||
CurvePanel* curve_panel_;
|
||||
|
||||
private slots:
|
||||
void SetDefaultLayout();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user