Commit 9a1da839 authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Effectstack: Add duplicate keyframe(s) button

parent b01d60d4
Pipeline #43427 passed with stage
in 10 minutes and 10 seconds
......@@ -134,6 +134,21 @@ bool KeyframeModel::removeKeyframe(GenTime pos, Fun &undo, Fun &redo, bool notif
return false;
}
bool KeyframeModel::duplicateKeyframe(GenTime srcPos, GenTime dstPos, Fun &undo, Fun &redo)
{
QWriteLocker locker(&m_lock);
Q_ASSERT(m_keyframeList.count(srcPos) > 0);
KeyframeType oldType = m_keyframeList[srcPos].first;
QVariant oldValue = m_keyframeList[srcPos].second;
Fun local_redo = addKeyframe_lambda(dstPos, oldType, oldValue, true);
Fun local_undo = deleteKeyframe_lambda(dstPos, true);
if (local_redo()) {
UPDATE_UNDO_REDO(local_redo, local_undo, undo, redo);
return true;
}
return false;
}
bool KeyframeModel::removeKeyframe(int frame)
{
GenTime pos(frame, pCore->getCurrentFps());
......
......@@ -81,6 +81,8 @@ protected:
/* @brief Removes the keyframe at the given position. */
bool removeKeyframe(int frame);
bool moveKeyframe(int oldPos, int pos, QVariant newVal);
/* @brief Duplicate a keyframe at the given position. */
bool duplicateKeyframe(GenTime srcPos, GenTime dstPos, Fun &undo, Fun &redo);
bool removeKeyframe(GenTime pos);
/* @brief Delete all the keyframes of the model */
bool removeAllKeyframes();
......
......@@ -160,6 +160,15 @@ bool KeyframeModelList::removeKeyframeWithUndo(GenTime pos, Fun &undo, Fun &redo
return result;
}
bool KeyframeModelList::duplicateKeyframeWithUndo(GenTime srcPos, GenTime destPos, Fun &undo, Fun &redo)
{
bool result = true;
for (const auto &param : m_parameters) {
result = result && param.second->duplicateKeyframe(srcPos, destPos, undo, redo);
}
return result;
}
bool KeyframeModelList::removeAllKeyframes()
{
QWriteLocker locker(&m_lock);
......
......@@ -65,6 +65,9 @@ public:
/* @brief Removes the keyframe at the given position. */
bool removeKeyframe(GenTime pos);
bool removeKeyframeWithUndo(GenTime pos, Fun &undo, Fun &redo);
/* @brief Duplicate a keyframe at the given position. */
bool duplicateKeyframeWithUndo(GenTime srcPos, GenTime destPos, Fun &undo, Fun &redo);
/* @brief Delete all the keyframes of the model (except first) */
bool removeAllKeyframes();
/* @brief Delete all the keyframes after a certain position (except first) */
......
......@@ -107,6 +107,20 @@ void KeyframeView::initKeyframePos()
emit atKeyframe(m_model->hasKeyframe(m_position), m_model->singleKeyframe());
}
void KeyframeView::slotDuplicateKeyframe()
{
int offset = pCore->getItemIn(m_model->getOwnerId());
if (m_currentKeyframe > -1 && !m_model->hasKeyframe(m_position + offset)) {
Fun undo = []() { return true; };
Fun redo = []() { return true; };
int delta = m_position - m_currentKeyframe;
for (int kf : m_selectedKeyframes) {
m_model->duplicateKeyframeWithUndo(GenTime(kf + offset, pCore->getCurrentFps()), GenTime(kf + delta + offset, pCore->getCurrentFps()), undo, redo);
}
pCore->pushUndo(undo, redo, i18n("Duplicate keyframe"));
}
}
void KeyframeView::slotAddKeyframe(int pos)
{
if (pos < 0) {
......@@ -219,9 +233,15 @@ void KeyframeView::slotCenterKeyframe()
if (!m_model->hasKeyframe(m_currentKeyframeOriginal + offset)) {
return;
}
GenTime initPos(m_currentKeyframeOriginal + offset, pCore->getCurrentFps());
GenTime targetPos(m_position + offset, pCore->getCurrentFps());
m_model->moveKeyframe(initPos, targetPos, true);
Fun undo = []() { return true; };
Fun redo = []() { return true; };
int delta = m_position - m_currentKeyframeOriginal;
for (int kf : m_selectedKeyframes) {
GenTime initPos(kf + offset, pCore->getCurrentFps());
GenTime targetPos(kf + delta + offset, pCore->getCurrentFps());
m_model->moveKeyframeWithUndo(initPos, targetPos, undo, redo);
}
pCore->pushUndo(undo, redo, i18n("Move keyframe"));
}
void KeyframeView::mousePressEvent(QMouseEvent *event)
......@@ -369,6 +389,7 @@ void KeyframeView::mouseMoveEvent(QMouseEvent *event)
int max = qMax(m_clickPoint, m_clickEnd);
min = qMax(1, min);
m_selectedKeyframes.clear();
m_currentKeyframeOriginal = m_currentKeyframe = -1;
double fps = pCore->getCurrentFps();
for (const auto &keyframe : *m_model.get()) {
int pos = keyframe.first.frames(fps) - offset;
......@@ -376,11 +397,14 @@ void KeyframeView::mouseMoveEvent(QMouseEvent *event)
m_selectedKeyframes << pos;
}
}
if (!m_selectedKeyframes.isEmpty()) {
m_currentKeyframeOriginal = m_currentKeyframe = m_selectedKeyframes.first();
}
update();
return;
}
if (KdenliveSettings::keyframeseek()) {
if (!m_moveKeyframeMode || KdenliveSettings::keyframeseek()) {
emit seekToPos(pos);
}
return;
......@@ -613,10 +637,12 @@ void KeyframeView::paintEvent(QPaintEvent *event)
if (scaledPos < m_zoomStart || qFloor(scaledPos) > zoomEnd) {
continue;
}
if (m_selectedKeyframes.contains(pos)) {
if (pos == m_currentKeyframe) {
p.setBrush(Qt::red);
} else if (m_selectedKeyframes.contains(pos)) {
p.setBrush(m_colSelected);
} else if (pos == m_currentKeyframe || pos == m_hoverKeyframe) {
p.setBrush(Qt::green);
p.setBrush(Qt::yellow);
} else {
p.setBrush(m_colKeyframe);
}
......
......@@ -47,6 +47,9 @@ public slots:
If pos is negative, then keyframe is added at current position
*/
void slotAddKeyframe(int pos = -1);
/* @brief Duplicate selected keyframe at cursor position
*/
void slotDuplicateKeyframe();
/* @brief If there is a keyframe at current position, it is removed.
Otherwise, we add a new one with given value.
*/
......
......@@ -86,6 +86,12 @@ KeyframeWidget::KeyframeWidget(std::shared_ptr<AssetParameterModel> model, QMode
m_buttonCenter->setIcon(QIcon::fromTheme(QStringLiteral("align-horizontal-center")));
m_buttonCenter->setToolTip(i18n("Move selected keyframe to cursor"));
// Duplicate selected keyframe at cursor pos
m_buttonCopy = new QToolButton(this);
m_buttonCopy->setAutoRaise(true);
m_buttonCopy->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
m_buttonCopy->setToolTip(i18n("Duplicate selected keyframe"));
// Keyframe type widget
m_selectType = new KSelectAction(QIcon::fromTheme(QStringLiteral("keyframes")), i18n("Keyframe interpolation"), this);
QAction *linear = new QAction(QIcon::fromTheme(QStringLiteral("linear")), i18n("Linear"), this);
......@@ -117,6 +123,7 @@ KeyframeWidget::KeyframeWidget(std::shared_ptr<AssetParameterModel> model, QMode
m_toolbar->addWidget(m_buttonAddDelete);
m_toolbar->addWidget(m_buttonNext);
m_toolbar->addWidget(m_buttonCenter);
m_toolbar->addWidget(m_buttonCopy);
m_toolbar->addAction(m_selectType);
QAction *seekKeyframe = new QAction(i18n("Seek to keyframe on select"), this);
......@@ -191,6 +198,7 @@ KeyframeWidget::KeyframeWidget(std::shared_ptr<AssetParameterModel> model, QMode
connect(m_buttonPrevious, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotGoToPrev);
connect(m_buttonNext, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotGoToNext);
connect(m_buttonCenter, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotCenterKeyframe);
connect(m_buttonCopy, &QAbstractButton::pressed, m_keyframeview, &KeyframeView::slotDuplicateKeyframe);
//m_baseHeight = m_keyframeview->height() + m_selectType->defaultWidget()->sizeHint().height();
QMargins mrg = m_lay->contentsMargins();
m_baseHeight = m_keyframeview->height() + m_toolbar->sizeHint().height() + mrg.top() + mrg.bottom();
......
......@@ -93,6 +93,7 @@ private:
QToolButton *m_buttonPrevious;
QToolButton *m_buttonNext;
QToolButton *m_buttonCenter;
QToolButton *m_buttonCopy;
KSelectAction *m_selectType;
TimecodeDisplay *m_time;
MonitorSceneType m_neededScene;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment