Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Fix keyframes reset, first draft of keyframes copy/paste

related to #1
parent 31ffb40a
......@@ -24,7 +24,7 @@ set(kdenlive_SRCS
assets/view/widgets/colorwheel.cpp
assets/view/widgets/slidewidget.cpp
assets/view/widgets/lumaliftgainparam.cpp
# assets/view/widgets/keyframeedit.cpp
assets/view/widgets/keyframeimport.cpp
assets/view/widgets/keyframewidget.cpp
assets/view/widgets/listparamwidget.cpp
assets/view/widgets/geometryeditwidget.cpp
......
......@@ -61,6 +61,7 @@ public:
enum { TypeRole = Qt::UserRole + 1, PosRole, FrameRole, ValueRole, NormalizedValueRole };
friend class KeyframeModelList;
friend class KeyframeWidget;
protected:
/** @brief These methods should ONLY be called by keyframemodellist to ensure synchronisation
......@@ -82,11 +83,11 @@ protected:
bool removeKeyframe(GenTime pos);
/* @brief Delete all the keyframes of the model */
bool removeAllKeyframes();
bool removeAllKeyframes(Fun &undo, Fun &redo);
bool removeAllKeyframes(Fun &undo, Fun &redo, bool notify = true);
protected:
/* @brief Same function but accumulates undo/redo */
bool removeKeyframe(GenTime pos, Fun &undo, Fun &redo);
bool removeKeyframe(GenTime pos, Fun &undo, Fun &redo, bool notify = true);
public:
/* @brief moves a keyframe
......@@ -107,6 +108,11 @@ public:
bool updateKeyframe(GenTime pos, QVariant value);
bool updateKeyframeType(GenTime pos, int type, Fun &undo, Fun &redo);
bool updateKeyframe(GenTime pos, QVariant value, Fun &undo, Fun &redo);
/* @brief updates the value of a keyframe, without any management of undo/redo
@param pos is the position of the keyframe
@param value is the new value of the param
*/
bool directUpdateKeyframe(GenTime pos, QVariant value);
/* @brief Returns a keyframe data at given pos
ok is a return parameter, set to true if everything went good
......@@ -141,6 +147,8 @@ public:
/* @brief Read the value from the model and update itself accordingly */
void refresh();
/* @brief Reset all values to their default */
void reset();
/* @brief Return the interpolated value at given pos */
QVariant getInterpolatedValue(int pos) const;
......@@ -152,6 +160,8 @@ public:
Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
static QList<QPoint> getRanges(const QString &animData);
static std::shared_ptr<Mlt::Properties> getAnimation(const QString &animData);
protected:
/** @brief Helper function that generate a lambda to change type / value of given keyframe */
......@@ -178,7 +188,9 @@ protected:
QString getAnimProperty() const;
QString getRotoProperty() const;
/* @brief this function does the opposite: given a MLT representation of an animation, build the corresponding model */
/* @brief this function clears all existing keyframes, and reloads its data from the string passed */
void resetAnimProperty(const QString &prop);
/* @brief this function does the opposite of getAnimProperty: given a MLT representation of an animation, build the corresponding model */
void parseAnimProperty(const QString &prop);
void parseRotoProperty(const QString &prop);
......
......@@ -21,6 +21,7 @@
#include "keyframemodellist.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "assets/model/assetcommand.hpp"
#include "core.h"
#include "doc/docundostack.hpp"
#include "keyframemodel.hpp"
......@@ -148,6 +149,16 @@ bool KeyframeModelList::updateKeyframe(GenTime oldPos, GenTime pos, double norma
bool KeyframeModelList::updateKeyframe(GenTime pos, QVariant value, const QPersistentModelIndex &index)
{
if (singleKeyframe()) {
bool ok = false;
Keyframe kf = m_parameters.begin()->second->getNextKeyframe(GenTime(-1), &ok);
pos = kf.first;
}
if (auto ptr = m_model.lock()) {
AssetKeyframeCommand *command = new AssetKeyframeCommand(ptr, index, value, pos);
pCore->pushUndo(command);
}
return true;
QWriteLocker locker(&m_lock);
Q_ASSERT(m_parameters.count(index) > 0);
Fun undo = []() { return true; };
......@@ -255,6 +266,14 @@ void KeyframeModelList::refresh()
}
}
void KeyframeModelList::reset()
{
QWriteLocker locker(&m_lock);
for (const auto &param : m_parameters) {
param.second->reset();
}
}
QVariant KeyframeModelList::getInterpolatedValue(int pos, const QPersistentModelIndex &index) const
{
READ_LOCK();
......@@ -270,6 +289,14 @@ KeyframeModel *KeyframeModelList::getKeyModel()
return nullptr;
}
KeyframeModel *KeyframeModelList::getKeyModel(const QPersistentModelIndex &index)
{
if (m_parameters.size() > 0) {
return m_parameters.at(index).get();
}
return nullptr;
}
void KeyframeModelList::resizeKeyframes(int oldIn, int oldOut, int in, int out, Fun &undo, Fun &redo)
{
bool ok;
......
......@@ -123,8 +123,12 @@ public:
@param index is the index of the queried parameter. */
QVariant getInterpolatedValue(int pos, const QPersistentModelIndex &index) const;
/* @brief Load keyframes from the current parameter value. */
void refresh();
/* @brief Reset all keyframes and add a default one */
void reset();
Q_INVOKABLE KeyframeModel *getKeyModel();
KeyframeModel *getKeyModel(const QPersistentModelIndex &index);
/** @brief Returns parent asset owner id*/
ObjectId getOwnerId() const;
......
......@@ -21,6 +21,7 @@
#include "assetcommand.hpp"
#include "effects/effectsrepository.hpp"
#include "assets/keyframes/model/keyframemodellist.hpp"
#include "transitions/transitionsrepository.hpp"
#include <memory>
......@@ -75,3 +76,51 @@ bool AssetCommand::mergeWith(const QUndoCommand *other)
m_stamp = static_cast<const AssetCommand *>(other)->m_stamp;
return true;
}
AssetKeyframeCommand::AssetKeyframeCommand(std::shared_ptr<AssetParameterModel> model, const QModelIndex &index, const QVariant &value, GenTime pos, QUndoCommand *parent)
: QUndoCommand(parent)
, m_model(model)
, m_index(index)
, m_value(value)
, m_pos(pos)
, m_updateView(false)
, m_stamp(QTime::currentTime())
{
const QString id = model->getAssetId();
if (EffectsRepository::get()->exists(id)) {
setText(i18n("Edit %1 keyframe", EffectsRepository::get()->getName(id)));
} else if (TransitionsRepository::get()->exists(id)) {
setText(i18n("Edit %1 keyframe", TransitionsRepository::get()->getName(id)));
}
m_oldValue = m_model->getKeyframeModel()->getKeyModel(m_index)->getInterpolatedValue(m_pos);
}
void AssetKeyframeCommand::undo()
{
m_model->getKeyframeModel()->getKeyModel(m_index)->directUpdateKeyframe(m_pos, m_oldValue);
}
// virtual
void AssetKeyframeCommand::redo()
{
m_model->getKeyframeModel()->getKeyModel(m_index)->directUpdateKeyframe(m_pos, m_value);
m_updateView = true;
}
// virtual
int AssetKeyframeCommand::id() const
{
return 2;
}
// virtual
bool AssetKeyframeCommand::mergeWith(const QUndoCommand *other)
{
if (other->id() != id() || static_cast<const AssetKeyframeCommand *>(other)->m_index != m_index ||
m_stamp.msecsTo(static_cast<const AssetKeyframeCommand *>(other)->m_stamp) > 1000) {
return false;
}
m_value = static_cast<const AssetKeyframeCommand *>(other)->m_value;
m_stamp = static_cast<const AssetKeyframeCommand *>(other)->m_stamp;
return true;
}
......@@ -46,4 +46,24 @@ private:
QTime m_stamp;
};
class AssetKeyframeCommand : public QUndoCommand
{
public:
AssetKeyframeCommand(std::shared_ptr<AssetParameterModel> model, const QModelIndex &index, const QVariant &value, GenTime pos, QUndoCommand *parent = nullptr);
void undo() override;
void redo() override;
int id() const override;
bool mergeWith(const QUndoCommand *other) override;
private:
std::shared_ptr<AssetParameterModel> m_model;
QPersistentModelIndex m_index;
QVariant m_value;
QVariant m_oldValue;
GenTime m_pos;
bool m_updateView;
QTime m_stamp;
};
#endif
......@@ -176,7 +176,7 @@ void AssetParameterModel::setParameter(const QString &name, const QString &value
Q_ASSERT(m_asset->is_valid());
QLocale locale;
locale.setNumberOptions(QLocale::OmitGroupSeparator);
qDebug()<<"// PROCESSING PARAM CHANGE!!! ";
bool conversionSuccess;
double doubleValue = locale.toDouble(value, &conversionSuccess);
if (conversionSuccess) {
......@@ -208,12 +208,13 @@ void AssetParameterModel::setParameter(const QString &name, const QString &value
// these effects don't understand param change and need to be rebuild
emit replugEffect(shared_from_this());
} else {
emit modelChanged();
qDebug()<<"// SENDING DATA CHANGE....";
if (paramIndex.isValid()) {
emit dataChanged(paramIndex, paramIndex, {});
emit dataChanged(paramIndex, paramIndex);
} else {
emit dataChanged(index(0, 0), index(m_rows.count() - 1, 0), {});
emit dataChanged(index(0, 0), index(m_rows.count(), 0));
}
emit modelChanged();
}
}
// Update timeline view if necessary
......
......@@ -86,11 +86,16 @@ void AssetParameterView::setModel(const std::shared_ptr<AssetParameterModel> &mo
void AssetParameterView::resetValues()
{
auto type = m_model->data(m_model->index(0, 0), AssetParameterModel::TypeRole).value<ParamType>();
for (int i = 0; i < m_model->rowCount(); ++i) {
QModelIndex index = m_model->index(i, 0);
QString name = m_model->data(index, AssetParameterModel::NameRole).toString();
ParamType type = m_model->data(index, AssetParameterModel::TypeRole).value<ParamType>();
QString defaultValue = m_model->data(index, AssetParameterModel::DefaultRole).toString();
if (type == ParamType::KeyframeParam || type == ParamType::AnimatedRect) {
if (!defaultValue.contains(QLatin1Char('='))) {
defaultValue.prepend(QStringLiteral("%1=").arg(m_model->data(index, AssetParameterModel::ParentInRole).toInt()));
}
}
m_model->setParameter(name, defaultValue);
if (m_mainKeyframeWidget) {
// Handles additionnal params like rotation so only refresh initial param at the end
......@@ -105,7 +110,7 @@ void AssetParameterView::resetValues()
}
}
if (m_mainKeyframeWidget) {
m_mainKeyframeWidget->slotRefresh();
m_mainKeyframeWidget->resetKeyframes();
}
}
......
......@@ -24,31 +24,36 @@
#include <QDialog>
#include <QLabel>
#include <QDoubleSpinBox>
#include "assets/model/assetparametermodel.hpp"
#include "definitions.h"
#include "timecode.h"
class PositionWidget;
class QComboBox;
class QCheckBox;
class QSpinBox;
class KeyframeView;
namespace Mlt {
class Properties;
}
class KeyframeImport : public QDialog
{
Q_OBJECT
public:
explicit KeyframeImport(const ItemInfo &srcInfo, const ItemInfo &dstInfo, const QMap<QString, QString> &data, const Timecode &tc, const QDomElement &xml,
QWidget *parent = nullptr);
explicit KeyframeImport(int in, int out, const QString &animData, std::shared_ptr<AssetParameterModel> model, QList <QPersistentModelIndex>indexes, QWidget *parent = nullptr);
virtual ~KeyframeImport();
QString selectedData() const;
QString selectedTarget() const;
private:
QDomElement m_xml;
std::shared_ptr<AssetParameterModel> m_model;
bool m_supportsAnim;
QComboBox *m_dataCombo;
QLabel *m_previewLabel;
KeyframeView *m_keyframeView;
PositionWidget *m_inPoint;
PositionWidget *m_outPoint;
PositionWidget *m_offsetPoint;
......@@ -66,6 +71,7 @@ private:
QMap<QString, QString> m_geometryTargets;
/** @brief Contains the 1 dimensional target parameter names / tag **/
QMap<QString, QString> m_simpleTargets;
void drawKeyFrameChannels(QPixmap &pix, int in, int out, int limitKeyframes, const QColor &textColor);
protected:
void resizeEvent(QResizeEvent *ev) override;
......
......@@ -23,6 +23,8 @@
#include "assets/keyframes/model/corners/cornershelper.hpp"
#include "assets/keyframes/view/keyframeview.hpp"
#include "assets/model/assetparametermodel.hpp"
#include "assets/model/assetcommand.hpp"
#include "assets/view/widgets/keyframeimport.h"
#include "core.h"
#include "monitor/monitor.h"
#include "timecode.h"
......@@ -33,7 +35,14 @@
#include <KSelectAction>
#include <QToolButton>
#include <QApplication>
#include <QClipboard>
#include <QVBoxLayout>
#include <QMenu>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QPointer>
#include <klocalizedstring.h>
KeyframeWidget::KeyframeWidget(std::shared_ptr<AssetParameterModel> model, QModelIndex index, QWidget *parent)
......@@ -96,6 +105,23 @@ KeyframeWidget::KeyframeWidget(std::shared_ptr<AssetParameterModel> model, QMode
m_toolbar->addWidget(m_buttonAddDelete);
m_toolbar->addWidget(m_buttonNext);
m_toolbar->addAction(m_selectType);
// copy/paste keyframes from clipboard
QAction *copy = new QAction(i18n("Copy keyframes to clipboard"), this);
connect(copy, &QAction::triggered, this, &KeyframeWidget::slotCopyKeyframes);
QAction *paste = new QAction(i18n("Import keyframes from clipboard"), this);
connect(paste, &QAction::triggered, this, &KeyframeWidget::slotImportKeyframes);
auto *container = new QMenu(this);
container->addAction(copy);
container->addAction(paste);
// Menu toolbutton
auto *menuButton = new QToolButton(this);
menuButton->setIcon(QIcon::fromTheme(QStringLiteral("kdenlive-menu")));
menuButton->setToolTip(i18n("Options"));
menuButton->setMenu(container);
menuButton->setPopupMode(QToolButton::InstantPopup);
m_toolbar->addWidget(menuButton);
m_toolbar->addWidget(m_time);
m_lay->addWidget(m_keyframeview);
......@@ -234,6 +260,22 @@ void KeyframeWidget::slotRefresh()
Q_ASSERT(ok);
// refresh keyframes
m_keyframes->refresh();
//m_model->dataChanged(QModelIndex(), QModelIndex());
//->getKeyframeModel()->getKeyModel(m_index)->dataChanged(QModelIndex(), QModelIndex());
m_keyframeview->setDuration(duration);
m_time->setRange(0, duration - 1);
slotRefreshParams();
}
void KeyframeWidget::resetKeyframes()
{
// update duration
bool ok = false;
int duration = m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt(&ok);
Q_ASSERT(ok);
// reset keyframes
m_keyframes->reset();
//m_model->dataChanged(QModelIndex(), QModelIndex());
m_keyframeview->setDuration(duration);
m_time->setRange(0, duration - 1);
slotRefreshParams();
......@@ -367,3 +409,73 @@ void KeyframeWidget::showKeyframes(bool enable)
m_toolbar->setVisible(enable);
m_keyframeview->setVisible(enable);
}
void KeyframeWidget::slotCopyKeyframes()
{
QJsonArray list;
for (const auto &w : m_parameters) {
int type = m_model->data(w.first, AssetParameterModel::TypeRole).toInt();
QString name = m_model->data(w.first, Qt::DisplayRole).toString();
QString value = m_model->data(w.first, AssetParameterModel::ValueRole).toString();
double min = m_model->data(w.first, AssetParameterModel::MinRole).toDouble();
double max = m_model->data(w.first, AssetParameterModel::MaxRole).toDouble();
double factor = m_model->data(w.first, AssetParameterModel::FactorRole).toDouble();
if (factor > 0) {
min /= factor;
max /= factor;
}
QJsonObject currentParam;
currentParam.insert(QLatin1String("name"), QJsonValue(name));
currentParam.insert(QLatin1String("value"), QJsonValue(value));
currentParam.insert(QLatin1String("type"), QJsonValue(type));
currentParam.insert(QLatin1String("min"), QJsonValue(min));
currentParam.insert(QLatin1String("max"), QJsonValue(max));
list.push_back(currentParam);
}
if (list.isEmpty()) {
return;
}
QClipboard *clipboard = QApplication::clipboard();
QJsonDocument json(list);
clipboard->setText(QString(json.toJson()));
}
void KeyframeWidget::slotImportKeyframes()
{
QClipboard *clipboard = QApplication::clipboard();
QString values = clipboard->text();
int inPos = m_model->data(m_index, AssetParameterModel::ParentInRole).toInt();
int outPos = inPos + m_model->data(m_index, AssetParameterModel::ParentDurationRole).toInt();
QList <QPersistentModelIndex>indexes;
for (const auto &w : m_parameters) {
indexes << w.first;
}
QPointer<KeyframeImport>import = new KeyframeImport(inPos, outPos, values, m_model, indexes, this);
if (import->exec() != QDialog::Accepted) {
delete import;
return;
}
QString keyframeData = import->selectedData();
QString tag = import->selectedTarget();
qDebug()<<"// CHECKING FOR TARGET PARAM: "<<tag;
//m_model->setParameter(tag, keyframeData, true);
/*for (const auto &w : m_parameters) {
qDebug()<<"// GOT PARAM: "<<m_model->data(w.first, AssetParameterModel::NameRole).toString();
if (tag == m_model->data(w.first, AssetParameterModel::NameRole).toString()) {
qDebug()<<"// PASSING DTAT: "<<keyframeData;
m_model->getKeyframeModel()->getKeyModel()->parseAnimProperty(keyframeData);
m_model->getKeyframeModel()->getKeyModel()->modelChanged();
break;
}
}*/
AssetCommand *command = new AssetCommand(m_model, m_index, keyframeData);
pCore->pushUndo(command);
/*m_model->getKeyframeModel()->getKeyModel()->dataChanged(QModelIndex(), QModelIndex());
m_model->modelChanged();
qDebug()<<"//// UPDATING KEYFRAMES CORE---------";
pCore->updateItemKeyframes(m_model->getOwnerId());*/
qDebug()<<"//// UPDATING KEYFRAMES CORE . .. .DONE ---------";
//emit importKeyframes(type, tag, keyframeData);
delete import;
}
......@@ -60,6 +60,7 @@ public:
/** @brief Returns true if keyframes options are visible
*/
bool keyframesVisible() const;
void resetKeyframes();
public slots:
void slotRefresh() override;
......@@ -77,6 +78,8 @@ private slots:
void monitorSeek(int pos);
void slotEditKeyframeType(QAction *action);
void slotUpdateKeyframesFromMonitor(QPersistentModelIndex index, const QVariant &res);
void slotCopyKeyframes();
void slotImportKeyframes();
private:
QVBoxLayout *m_lay;
......@@ -95,6 +98,7 @@ private:
signals:
void addIndex(QPersistentModelIndex ix);
void setKeyframes(const QString &);
};
#endif
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