Git commit 443776470b9e909230ec2f192909205143ce3113 by Dmitry Kazakov.
Committed on 11/04/2018 at 16:06.
Pushed by dkazakov into branch 'master'.
Implement Add/Remove Hold Frames feature
These actions allow the user change duration of the key frames that
are part of the current selection.
CC:kimageshop@kde.org
M +51 -0 krita/krita.action
M +93 -0 plugins/dockers/animation/timeline_frames_model.cpp
M +2 -0 plugins/dockers/animation/timeline_frames_model.h
M +82 -0 plugins/dockers/animation/timeline_frames_view.cpp
M +6 -0 plugins/dockers/animation/timeline_frames_view.h
https://commits.kde.org/krita/443776470b9e909230ec2f192909205143ce3113
diff --git a/krita/krita.action b/krita/krita.action
index cdca2e1f2b5..41beb61392c 100644
--- a/krita/krita.action
+++ b/krita/krita.action
@@ -2344,6 +2344,57 @@
=
+
+
+ Insert Hold Frame
+
+ Insert a hold frame after every keyframe
+
+ 100000
+ 0
+
+ false
+
+
+
+
+
+ Insert N Hold Frames
+
+ Insert N hold frames after every keyframe
+
+ 100000
+ 0
+
+ false
+
+
+
+
+
+ Remove Hold Frame
+
+ Insert a hold frame after every keyframe
+
+ 100000
+ 0
+
+ false
+
+
+
+
+
+ Remove N Hold Frames
+
+ Remove N hold frames after every keyframe
+
+ 100000
+ 0
+
+ false
+
+
=
=
diff --git a/plugins/dockers/animation/timeline_frames_model.cpp b/plugins/=
dockers/animation/timeline_frames_model.cpp
index 6e28cc872ed..f98e1473393 100644
--- a/plugins/dockers/animation/timeline_frames_model.cpp
+++ b/plugins/dockers/animation/timeline_frames_model.cpp
@@ -739,6 +739,99 @@ bool TimelineFramesModel::insertFrames(int dstColumn, =
const QList &dstRows,
return true;
}
=
+bool TimelineFramesModel::insertHoldFrames(QModelIndexList selectedIndexes=
, int count)
+{
+ if (selectedIndexes.isEmpty() || count =3D=3D 0) return true;
+
+ QScopedPointer parentCommand(new KUndo2Command(kundo2_i=
18np("Insert frame", "Insert %1 frames", count)));
+
+ {
+ KisImageBarrierLockerWithFeedback locker(m_d->image);
+
+ QSet uniqueKeyframesInSelection;
+
+ Q_FOREACH (const QModelIndex &index, selectedIndexes) {
+ KisNodeSP node =3D nodeAt(index);
+ KIS_SAFE_ASSERT_RECOVER(node) { continue; }
+
+ KisKeyframeChannel *channel =3D node->getKeyframeChannel(KisKe=
yframeChannel::Content.id());
+ if (!channel) continue;
+
+ KisKeyframeSP keyFrame =3D channel->activeKeyframeAt(index.col=
umn());
+
+ if (keyFrame) {
+ uniqueKeyframesInSelection.insert(keyFrame);
+ }
+ }
+
+ int minSelectedTime =3D std::numeric_limits::max();
+ QList keyframesToMove;
+
+ for (auto it =3D uniqueKeyframesInSelection.begin(); it !=3D uniqu=
eKeyframesInSelection.end(); ++it) {
+ KisKeyframeSP keyframe =3D *it;
+
+ minSelectedTime =3D qMin(minSelectedTime, keyframe->time());
+
+ KisKeyframeChannel *channel =3D keyframe->channel();
+ KisKeyframeSP nextKeyframe =3D channel->nextKeyframe(keyframe);
+
+ if (nextKeyframe) {
+ keyframesToMove << nextKeyframe;
+ }
+ }
+
+ std::sort(keyframesToMove.begin(), keyframesToMove.end(),
+ [] (KisKeyframeSP lhs, KisKeyframeSP rhs) {
+ return lhs->time() > rhs->time();
+ });
+
+ if (keyframesToMove.isEmpty()) return true;
+
+ const int maxColumn =3D columnCount();
+
+ if (count > 0) {
+ setLastVisibleFrame(columnCount() + count);
+ }
+
+ Q_FOREACH (KisKeyframeSP keyframe, keyframesToMove) {
+ int plannedFrameMove =3D count;
+
+ if (count < 0) {
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(keyframe->time() > 0,=
false);
+
+ KisKeyframeSP prevFrame =3D keyframe->channel()->previousK=
eyframe(keyframe);
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(prevFrame, false);
+
+ plannedFrameMove =3D qMax(count, prevFrame->time() - keyfr=
ame->time() + 1);
+ }
+
+ KisNodeDummy *dummy =3D m_d->dummiesFacade->dummyForNode(keyfr=
ame->channel()->node());
+ KIS_SAFE_ASSERT_RECOVER(dummy) { continue; }
+
+ const int row =3D m_d->converter->rowForDummy(dummy);
+ KIS_SAFE_ASSERT_RECOVER(row >=3D 0) { continue; }
+
+ QModelIndexList indexes;
+ for (int column =3D keyframe->time(); column < maxColumn; colu=
mn++) {
+ indexes << index(row, column);
+ }
+
+ createOffsetFramesCommand(indexes, QPoint(plannedFrameMove, 0)=
, false, parentCommand.data());
+ }
+
+ const int oldTime =3D m_d->image->animationInterface()->currentUIT=
ime();
+ const int newTime =3D minSelectedTime;
+
+ new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(),
+ oldTime,
+ newTime, parentCommand.data());
+ }
+
+
+ KisProcessingApplicator::runSingleCommandStroke(m_d->image, parentComm=
and.take(), KisStrokeJobData::BARRIER);
+ return true;
+}
+
=
QString TimelineFramesModel::audioChannelFileName() const
{
diff --git a/plugins/dockers/animation/timeline_frames_model.h b/plugins/do=
ckers/animation/timeline_frames_model.h
index 2c7908438fe..0f053c29587 100644
--- a/plugins/dockers/animation/timeline_frames_model.h
+++ b/plugins/dockers/animation/timeline_frames_model.h
@@ -53,6 +53,8 @@ public:
=
bool insertFrames(int dstColumn, const QList &dstRows, int count);
=
+ bool insertHoldFrames(QModelIndexList selectedIndexes, int count);
+
QString audioChannelFileName() const;
void setAudioChannelFileName(const QString &fileName);
=
diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/d=
ockers/animation/timeline_frames_view.cpp
index d9a769bf9a6..e997ee1768b 100644
--- a/plugins/dockers/animation/timeline_frames_view.cpp
+++ b/plugins/dockers/animation/timeline_frames_view.cpp
@@ -311,6 +311,18 @@ void TimelineFramesView::setActionManager( KisActionMa=
nager * actionManager)
=
action =3D m_d->actionMan->createAction("remove_frames");
connect(action, SIGNAL(triggered()), SLOT(slotRemoveFrame()));
+
+ action =3D m_d->actionMan->createAction("add_hold_frame");
+ connect(action, SIGNAL(triggered()), SLOT(slotInsertHoldFrames()));
+
+ action =3D m_d->actionMan->createAction("add_n_hold_frames");
+ connect(action, SIGNAL(triggered()), SLOT(slotInsertHoldFramesCust=
om()));
+
+ action =3D m_d->actionMan->createAction("remove_hold_frame");
+ connect(action, SIGNAL(triggered()), SLOT(slotRemoveHoldFrames()));
+
+ action =3D m_d->actionMan->createAction("remove_n_hold_frames");
+ connect(action, SIGNAL(triggered()), SLOT(slotRemoveHoldFramesCust=
om()));
}
}
=
@@ -936,6 +948,12 @@ void TimelineFramesView::mousePressEvent(QMouseEvent *=
event)
addActionToMenu(&menu, "remove_frames");
addActionToMenu(&menu, "remove_frames_and_shift");
menu.addSeparator();
+ addActionToMenu(&menu, "add_hold_frame");
+ addActionToMenu(&menu, "remove_hold_frame");
+ menu.addSeparator();
+ addActionToMenu(&menu, "add_n_hold_frames");
+ addActionToMenu(&menu, "remove_n_hold_frames");
+ menu.addSeparator();
menu.addAction(m_d->colorSelectorAction);
menu.exec(event->globalPos());
=
@@ -959,6 +977,12 @@ void TimelineFramesView::mousePressEvent(QMouseEvent *=
event)
menu.addSeparator();
addActionToMenu(&menu, "remove_frames_and_shift");
menu.addSeparator();
+ addActionToMenu(&menu, "add_hold_frame");
+ addActionToMenu(&menu, "remove_hold_frame");
+ menu.addSeparator();
+ addActionToMenu(&menu, "add_n_hold_frames");
+ addActionToMenu(&menu, "remove_n_hold_frames");
+ menu.addSeparator();
menu.addAction(m_d->colorSelectorAction);
menu.exec(event->globalPos());
}
@@ -996,6 +1020,14 @@ void TimelineFramesView::mousePressEvent(QMouseEvent =
*event)
addActionToMenu(&menu, "remove_frames");
}
addActionToMenu(&menu, "remove_frames_and_shift");
+
+ menu.addSeparator();
+ addActionToMenu(&menu, "add_hold_frame");
+ addActionToMenu(&menu, "remove_hold_frame");
+ menu.addSeparator();
+ addActionToMenu(&menu, "add_n_hold_frames");
+ addActionToMenu(&menu, "remove_n_hold_frames");
+
if (haveFrames) {
menu.addSeparator();
menu.addAction(m_d->multiframeColorSelectorAction);
@@ -1339,6 +1371,56 @@ void TimelineFramesView::slotRemoveColumnsAndShift()
slotRemoveFramesAndShift(true);
}
=
+void TimelineFramesView::slotInsertHoldFrames(int count)
+{
+ QModelIndexList indexes;
+
+ Q_FOREACH (const QModelIndex &index, selectionModel()->selectedIndexes=
()) {
+ if (m_d->model->data(index, TimelineFramesModel::FrameEditableRole=
).toBool()) {
+ indexes << index;
+ }
+ }
+
+ if (!indexes.isEmpty()) {
+ m_d->model->insertHoldFrames(indexes, count);
+ }
+}
+
+void TimelineFramesView::slotRemoveHoldFrames(int count)
+{
+ slotInsertHoldFrames(-count);
+}
+
+void TimelineFramesView::slotInsertHoldFramesCustom()
+{
+ bool ok =3D false;
+
+ // TODO: save previous entered value till the end of the session
+ const int count =3D QInputDialog::getInt(this,
+ i18nc("@title:window", "Insert =
hold frames"),
+ i18nc("@label:spinbox", "Enter =
number of frames"),
+ 1, 1, 10000, 1, &ok);
+
+ if (ok) {
+ slotInsertHoldFrames(count);
+ }
+}
+
+void TimelineFramesView::slotRemoveHoldFramesCustom()
+{
+ bool ok =3D false;
+
+ // TODO: save previous entered value till the end of the session
+ const int count =3D QInputDialog::getInt(this,
+ i18nc("@title:window", "Remove =
hold frames"),
+ i18nc("@label:spinbox", "Enter =
number of frames"),
+ 1, 1, 10000, 1, &ok);
+
+ if (ok) {
+ slotRemoveHoldFrames(count);
+ }
+}
+
=
bool TimelineFramesView::viewportEvent(QEvent *event)
{
diff --git a/plugins/dockers/animation/timeline_frames_view.h b/plugins/doc=
kers/animation/timeline_frames_view.h
index 1290068e81e..47d4762dcfe 100644
--- a/plugins/dockers/animation/timeline_frames_view.h
+++ b/plugins/dockers/animation/timeline_frames_view.h
@@ -80,6 +80,12 @@ private Q_SLOTS:
void slotRemoveColumns();
void slotRemoveColumnsAndShift();
=
+ void slotInsertHoldFrames(int count =3D 1);
+ void slotInsertHoldFramesCustom();
+
+ void slotRemoveHoldFrames(int count =3D 1);
+ void slotRemoveHoldFramesCustom();
+
void slotReselectCurrentIndex();
=
void slotUpdateInfiniteFramesCount();