implemented the ability to modify bezier points in the curve editor
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user