Fix disappearing keyframes in animated parameters (Transform effect and...

Fix disappearing keyframes in animated parameters (Transform effect and Composite+Transform transition)
parent 5933f52d
......@@ -552,6 +552,7 @@ void CollapsibleEffect::setupWidget(const ItemInfo &info, EffectMetaInfo *metaIn
else {
m_paramWidget = new ParameterContainer(m_effect, info, metaInfo, widgetFrame);
connect(m_paramWidget, SIGNAL(disableCurrentFilter(bool)), this, SLOT(slotDisableEffect(bool)));
connect(m_paramWidget, &ParameterContainer::importKeyframes, this, &CollapsibleEffect::importKeyframes);
if (m_effect.firstChildElement(QStringLiteral("parameter")).isNull()) {
// Effect has no parameter, don't allow expand
collapseButton->setEnabled(false);
......@@ -627,6 +628,13 @@ void CollapsibleEffect::dragLeaveEvent(QDragLeaveEvent */*event*/)
frame->setStyleSheet(frame->styleSheet());
}
void CollapsibleEffect::importKeyframes(const QString &keyframes)
{
QMap <QString, QString> data;
data.insert(i18n("Geometry"), keyframes);
emit importClipKeyframes(AVWidget, m_itemInfo, m_effect.cloneNode().toElement(), data);
}
void CollapsibleEffect::dropEvent(QDropEvent *event)
{
if (event->mimeData()->hasFormat(QStringLiteral("kdenlive/geometry"))) {
......@@ -635,9 +643,7 @@ void CollapsibleEffect::dropEvent(QDropEvent *event)
}
emit activateEffect(effectIndex());
QString itemData = event->mimeData()->data(QStringLiteral("kdenlive/geometry"));
QMap <QString, QString> data;
data.insert(i18n("Geometry"), itemData);
emit importClipKeyframes(AVWidget, m_itemInfo, m_effect.cloneNode().toElement(), data);
importKeyframes(itemData);
return;
}
frame->setProperty("target", false);
......
......@@ -91,7 +91,8 @@ public slots:
void slotSyncEffectsPos(int pos);
void slotDisable(bool disable, bool emitInfo = true);
void slotResetEffect();
void importKeyframes(const QString &keyframes);
private slots:
void setWidgetHeight(qreal value);
......
......@@ -360,6 +360,7 @@ ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo
} else {
m_animationWidget = new AnimationWidget(m_metaInfo, info.startPos.frames(KdenliveSettings::project_fps()), m_in, m_out, effect.attribute(QStringLiteral("in")).toInt(), effect.attribute(QStringLiteral("id")), pa, parent);
connect(m_animationWidget, SIGNAL(seekToPos(int)), this, SIGNAL(seekTimeline(int)));
connect(m_animationWidget, &AnimationWidget::setKeyframes, this, &ParameterContainer::importKeyframes);
connect(this, SIGNAL(syncEffectsPos(int)), m_animationWidget, SLOT(slotSyncPosition(int)));
connect(this, SIGNAL(initScene(int)), m_animationWidget, SLOT(slotPositionChanged(int)));
connect(m_animationWidget, SIGNAL(parameterChanged()), this, SLOT(slotCollectAllParameters()));
......
......@@ -152,6 +152,7 @@ signals:
void checkMonitorPosition(int);
void seekTimeline(int);
void showComments(bool);
void importKeyframes(const QString &);
/** @brief Start an MLT filter job on this clip.
* @param filterParams a QMap containing filter name under the "filter" key, and all filter properties
* @param consumerParams a QMap containing consumer name under the "consumer" key, and all consumer properties
......
......@@ -34,6 +34,7 @@
#include <QDialogButtonBox>
#include <QDragEnterEvent>
#include <QMimeData>
#include <QClipboard>
#include <KDualAction>
#include <KConfig>
......@@ -157,6 +158,12 @@ AnimationWidget::AnimationWidget(EffectMetaInfo *info, int clipPos, int min, int
m_endAttach->setCheckable(true);
connect(m_endAttach, &QAction::toggled, this, &AnimationWidget::slotReverseKeyframeType);
// copy/paste keyframes from clipboard
QAction *copy = new QAction(i18n("Copy keyframes to clipboard"), this);
connect(copy, &QAction::triggered, this, &AnimationWidget::slotCopyKeyframes);
QAction *paste = new QAction(i18n("Import keyframes from clipboard"), this);
connect(paste, &QAction::triggered, this, &AnimationWidget::slotImportKeyframes);
// save preset
QAction *savePreset = new QAction(KoIconUtils::themedIcon(QStringLiteral("document-save")), i18n("Save preset"), this);
connect(savePreset, &QAction::triggered, this, &AnimationWidget::savePreset);
......@@ -168,6 +175,10 @@ AnimationWidget::AnimationWidget(EffectMetaInfo *info, int clipPos, int min, int
QMenu *container = new QMenu;
tb->addAction(m_selectType);
container->addAction(m_endAttach);
container->addSeparator();
container->addAction(copy);
container->addAction(paste);
container->addSeparator();
container->addAction(savePreset);
container->addAction(delPreset);
container->addAction(defaultInterp);
......@@ -262,7 +273,12 @@ void AnimationWidget::slotNext()
slotPositionChanged(next, true);
}
void AnimationWidget::slotAddKeyframe(int pos, QString paramName, bool directUpdate)
void AnimationWidget::slotAddKeyframe(int pos)
{
slotAddDeleteKeyframe(true, pos);
}
void AnimationWidget::doAddKeyframe(int pos, QString paramName, bool directUpdate)
{
if (paramName.isEmpty()) {
paramName = m_inTimeline;
......@@ -327,7 +343,7 @@ void AnimationWidget::slotAddDeleteKeyframe(bool add, int pos)
for (int i = 0; i < paramNames.count(); i++) {
m_animController = m_animProperties.get_animation(paramNames.at(i).toUtf8().constData());
if (!m_animController.is_key(pos - m_offset)) {
slotAddKeyframe(pos - m_offset, paramNames.at(i), false);
doAddKeyframe(pos - m_offset, paramNames.at(i), false);
}
}
m_ruler->setActiveKeyframe(pos);
......@@ -718,7 +734,7 @@ void AnimationWidget::buildRectWidget(const QString &paramTag, const QDomElement
connect(fitToWidth, SIGNAL(triggered()), this, SLOT(slotFitToWidth()));
QAction *fitToHeight = new QAction(KoIconUtils::themedIcon(QStringLiteral("zoom-fit-height")), i18n("Fit to height"), this);
connect(fitToHeight, SIGNAL(triggered()), this, SLOT(slotFitToHeight()));
QAction *alignleft = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-left")), i18n("Align left"), this);
connect(alignleft, SIGNAL(triggered()), this, SLOT(slotMoveLeft()));
QAction *alignhcenter = new QAction(KoIconUtils::themedIcon(QStringLiteral("kdenlive-align-hor")), i18n("Center horizontally"), this);
......@@ -1186,15 +1202,72 @@ void AnimationWidget::offsetAnimation(int offset)
void AnimationWidget::reload(const QString &tag, const QString &data)
{
m_animProperties.set(tag.toUtf8().constData(), data.toUtf8().constData());
m_animProperties.anim_get_int(tag.toUtf8().constData(), 0, m_outPoint);
m_attachedToEnd = KeyframeView::checkNegatives(data, m_outPoint);
m_inTimeline = tag;
QMapIterator<QString, DoubleParameterWidget *> i(m_doubleWidgets);
while (i.hasNext()) {
i.next();
i.value()->setChecked(i.key() == tag);
if (tag == m_rectParameter) {
m_animProperties.anim_get_rect(tag.toUtf8().constData(), 0, m_outPoint);
} else {
m_animProperties.anim_get_int(tag.toUtf8().constData(), 0, m_outPoint);
m_attachedToEnd = KeyframeView::checkNegatives(data, m_outPoint);
m_inTimeline = tag;
QMapIterator<QString, DoubleParameterWidget *> i(m_doubleWidgets);
while (i.hasNext()) {
i.next();
i.value()->setChecked(i.key() == tag);
}
}
// Also add keyframes positions in other parameters
QStringList paramNames = m_doubleWidgets.keys();
QLocale locale;
m_animController = m_animProperties.get_animation(tag.toUtf8().constData());
for (int i = 0; i < paramNames.count(); i++) {
QString currentParam = paramNames.at(i);
if (currentParam == tag) continue;
// simple anim parameter, get default value
double def = locale.toDouble(defaultValue(currentParam));
// Clear current keyframes
m_animProperties.set(currentParam.toUtf8().constData(), "");
// Add default keyframes
int pos;
mlt_keyframe_type type;
for(int j = 0; j < m_animController.key_count(); ++j) {
m_animController.key_get(j, pos, type);
m_animProperties.anim_set(currentParam.toUtf8().constData(), def, pos, m_outPoint, type);
}
m_animProperties.anim_get_int(currentParam.toUtf8().constData(), 0, m_outPoint);
}
if (!m_rectParameter.isEmpty() && tag != m_rectParameter) {
// reset geometry keyframes
// simple anim parameter, get default value
QString def = getDefaultKeyframes(defaultValue(m_rectParameter));
// Clear current keyframes
m_animProperties.set(m_rectParameter.toUtf8().constData(), def.toUtf8().constData());
// Add default keyframes
int pos;
mlt_keyframe_type type;
m_animProperties.anim_get_rect(m_rectParameter.toUtf8().constData(), 0, m_outPoint);
mlt_rect rect = m_animProperties.anim_get_rect(m_rectParameter.toUtf8().constData(), 0, m_outPoint);
for(int j = 0; j < m_animController.key_count(); ++j) {
m_animController.key_get(j, pos, type);
m_animProperties.anim_set(m_rectParameter.toUtf8().constData(), rect, pos, m_outPoint, type);
}
m_animProperties.anim_get_rect(m_rectParameter.toUtf8().constData(), 0, m_outPoint);
}
rebuildKeyframes();
emit parameterChanged();
}
QString AnimationWidget::defaultValue(const QString &paramName)
{
QStringList paramNames = m_doubleWidgets.keys();
for (int i = 0; i < paramNames.count(); i++) {
if (m_params.at(i).attribute(QStringLiteral("name")) == paramName) {
QString def = m_params.at(i).attribute(QStringLiteral("default"));
if (def.contains(QLatin1Char('%'))) {
def = EffectsController::getStringRectEval(m_monitor->profileInfo(), def);
}
return def;
}
}
return QString();
}
void AnimationWidget::slotAdjustToSource()
......@@ -1303,3 +1376,25 @@ void AnimationWidget::slotMoveBottom()
{
m_spinY->setValue(m_monitor->render->renderHeight() - m_spinHeight->value());
}
void AnimationWidget::slotCopyKeyframes()
{
const QMap <QString, QString> anims = getAnimation();
if (anims.isEmpty())
return;
QString value;
if (anims.count() == 1) {
value = anims.first();
} else {
value = anims.value(m_inTimeline);
}
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(value);
}
void AnimationWidget::slotImportKeyframes()
{
QClipboard *clipboard = QApplication::clipboard();
QString values = clipboard->text();
emit setKeyframes(values);
}
......@@ -110,6 +110,10 @@ private:
void buildRectWidget(const QString &paramTag, const QDomElement &e);
/** @brief Calculate path for keyframes centers and send to monitor */
void setupMonitor(QRect r = QRect());
/** @brief Returns the default value for a parameter from its name */
QString defaultValue(const QString &paramName);
/** @brief Add a keyframe in all geometries */
void doAddKeyframe(int pos, QString paramName, bool directUpdate);
public slots:
void slotSyncPosition(int relTimelinePos);
......@@ -123,12 +127,14 @@ private slots:
void slotEditKeyframeType(QAction *action);
void slotAdjustKeyframeValue(double value);
void slotAdjustRectKeyframeValue();
void slotAddKeyframe(int pos = -1, QString paramName = QString(), bool directUpdate = true);
void slotAddKeyframe(int pos = -1);
void slotDeleteKeyframe(int pos = -1);
void slotReverseKeyframeType(bool reverse);
void applyPreset(int ix);
void savePreset();
void deletePreset();
void slotCopyKeyframes();
void slotImportKeyframes();
void slotSetDefaultInterp(QAction *action);
void slotUpdateVisibleParameter(bool display);
void slotUpdateGeometryRect(const QRect r);
......@@ -155,6 +161,8 @@ private slots:
signals:
void seekToPos(int);
void parameterChanged();
/** @brief keyframes dropped / pasted on widget, import them. */
void setKeyframes(const QString &);
};
#endif
......@@ -1803,7 +1803,7 @@ void MainWindow::connectDocument()
connect(trackView, SIGNAL(showTrackEffects(int,TrackInfo)), this, SLOT(slotTrackSelected(int,TrackInfo)));
connect(trackView->projectView(), SIGNAL(clipItemSelected(ClipItem*,bool,bool)), this, SLOT(slotTimelineClipSelected(ClipItem*,bool,bool)), Qt::DirectConnection);
connect(trackView->projectView(), SIGNAL(clipItemSelected(ClipItem*,bool)), this, SLOT(slotTimelineClipSelected(ClipItem*,bool)), Qt::DirectConnection);
connect(trackView->projectView(), &CustomTrackView::setActiveKeyframe, m_effectStack, &EffectStackView2::setActiveKeyframe);
connect(trackView->projectView(), SIGNAL(transitionItemSelected(Transition*,int,QPoint,bool)), m_effectStack, SLOT(slotTransitionItemSelected(Transition*,int,QPoint,bool)), Qt::DirectConnection);
......@@ -2558,13 +2558,10 @@ void MainWindow::customEvent(QEvent* e)
m_messageLabel->setMessage(static_cast <MltErrorEvent *>(e)->message(), MltError);
}
void MainWindow::slotTimelineClipSelected(ClipItem* item, bool reloadStack, bool raise)
void MainWindow::slotTimelineClipSelected(ClipItem* item, bool reloadStack)
{
m_effectStack->slotClipItemSelected(item, m_projectMonitor, reloadStack);
m_projectMonitor->slotSetSelectedClip(item);
/*if (raise) {
m_effectStack->raiseWindow(m_effectStackDock);
}*/
}
void MainWindow::slotTrackSelected(int index, const TrackInfo &info, bool raise)
......
......@@ -256,7 +256,7 @@ public slots:
void slotPreferences(int page = -1, int option = -1);
void connectDocument();
void slotTimelineClipSelected(ClipItem* item, bool reloadStack = true, bool raise = true);
void slotTimelineClipSelected(ClipItem* item, bool reloadStack = true);
/** @brief Reload project profile in config dialog if changed. */
void slotRefreshProfiles();
void updateDockTitleBars(bool isTopLevel = true);
......
......@@ -592,7 +592,7 @@ signals:
/** @brief A clip was selected in timeline, update the effect stack
* @param clip The clip
* @param raise If true, the effect stack widget will be raised (come to front). */
void clipItemSelected(ClipItem *clip, bool reloadStack = true, bool raise = true);
void clipItemSelected(ClipItem *clip, bool reloadStack = true);
void transitionItemSelected(Transition*, int track = 0, QPoint p = QPoint(), bool update = false);
void activateDocumentMonitor();
void tracksChanged();
......
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