[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-kimageshop
Subject:    [krita] /: Implement Add/Remove Hold Frames feature
From:       Dmitry Kazakov <null () kde ! org>
Date:       2018-04-11 16:07:50
Message-ID: E1f6IHm-0005jt-FJ () code ! kde ! org
[Download RAW message or body]

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 @@
       <statusTip></statusTip>
     </Action>
 
+    <Action name="add_hold_frame">
+      <icon></icon>
+      <text>Insert Hold Frame</text>
+      <whatsThis></whatsThis>
+      <toolTip>Insert a hold frame after every keyframe</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="add_n_hold_frames">
+      <icon></icon>
+      <text>Insert N Hold Frames</text>
+      <whatsThis></whatsThis>
+      <toolTip>Insert N hold frames after every keyframe</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="remove_hold_frame">
+      <icon></icon>
+      <text>Remove Hold Frame</text>
+      <whatsThis></whatsThis>
+      <toolTip>Insert a hold frame after every keyframe</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
+
+    <Action name="remove_n_hold_frames">
+      <icon></icon>
+      <text>Remove N Hold Frames</text>
+      <whatsThis></whatsThis>
+      <toolTip>Remove N hold frames after every keyframe</toolTip>
+      <iconText></iconText>
+      <activationFlags>100000</activationFlags>
+      <activationConditions>0</activationConditions>
+      <shortcut></shortcut>
+      <isCheckable>false</isCheckable>
+      <statusTip></statusTip>
+    </Action>
 
   </Actions>
 
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<int> &dstRows,  return true;
 }
 
+bool TimelineFramesModel::insertHoldFrames(QModelIndexList \
selectedIndexes, int count) +{
+    if (selectedIndexes.isEmpty() || count == 0) return true;
+
+    QScopedPointer<KUndo2Command> parentCommand(new \
KUndo2Command(kundo2_i18np("Insert frame", "Insert %1 frames", count))); +
+    {
+        KisImageBarrierLockerWithFeedback locker(m_d->image);
+
+        QSet<KisKeyframeSP> uniqueKeyframesInSelection;
+
+        Q_FOREACH (const QModelIndex &index, selectedIndexes) {
+            KisNodeSP node = nodeAt(index);
+            KIS_SAFE_ASSERT_RECOVER(node) { continue; }
+
+            KisKeyframeChannel *channel = \
node->getKeyframeChannel(KisKeyframeChannel::Content.id()); +            if \
(!channel) continue; +
+            KisKeyframeSP keyFrame = \
channel->activeKeyframeAt(index.column()); +
+            if (keyFrame) {
+                uniqueKeyframesInSelection.insert(keyFrame);
+            }
+        }
+
+        int minSelectedTime = std::numeric_limits<int>::max();
+        QList<KisKeyframeSP> keyframesToMove;
+
+        for (auto it = uniqueKeyframesInSelection.begin(); it != \
uniqueKeyframesInSelection.end(); ++it) { +            KisKeyframeSP \
keyframe = *it; +
+            minSelectedTime = qMin(minSelectedTime, keyframe->time());
+
+            KisKeyframeChannel *channel = keyframe->channel();
+            KisKeyframeSP nextKeyframe = 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 = columnCount();
+
+        if (count > 0) {
+            setLastVisibleFrame(columnCount() + count);
+        }
+
+        Q_FOREACH (KisKeyframeSP keyframe, keyframesToMove) {
+            int plannedFrameMove = count;
+
+            if (count < 0) {
+                KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(keyframe->time() > 0, \
false); +
+                KisKeyframeSP prevFrame = \
keyframe->channel()->previousKeyframe(keyframe); +                \
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(prevFrame, false); +
+                plannedFrameMove = qMax(count, prevFrame->time() - \
keyframe->time() + 1); +            }
+
+            KisNodeDummy *dummy = \
m_d->dummiesFacade->dummyForNode(keyframe->channel()->node()); +            \
KIS_SAFE_ASSERT_RECOVER(dummy) { continue; } +
+            const int row = m_d->converter->rowForDummy(dummy);
+            KIS_SAFE_ASSERT_RECOVER(row >= 0) { continue; }
+
+            QModelIndexList indexes;
+            for (int column = keyframe->time(); column < maxColumn; \
column++) { +                indexes << index(row, column);
+            }
+
+            createOffsetFramesCommand(indexes, QPoint(plannedFrameMove, \
0), false, parentCommand.data()); +        }
+
+        const int oldTime = \
m_d->image->animationInterface()->currentUITime(); +        const int \
newTime = minSelectedTime; +
+        new KisSwitchCurrentTimeCommand(m_d->image->animationInterface(),
+                                        oldTime,
+                                        newTime, parentCommand.data());
+    }
+
+
+    KisProcessingApplicator::runSingleCommandStroke(m_d->image, \
parentCommand.take(), KisStrokeJobData::BARRIER); +    return true;
+}
+
 
 QString TimelineFramesModel::audioChannelFileName() const
 {
diff --git a/plugins/dockers/animation/timeline_frames_model.h \
b/plugins/dockers/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<int> &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/dockers/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( \
KisActionManager * actionManager)  
         action = m_d->actionMan->createAction("remove_frames");
         connect(action, SIGNAL(triggered()), SLOT(slotRemoveFrame()));
+
+        action = m_d->actionMan->createAction("add_hold_frame");
+        connect(action, SIGNAL(triggered()), \
SLOT(slotInsertHoldFrames())); +
+        action = m_d->actionMan->createAction("add_n_hold_frames");
+        connect(action, SIGNAL(triggered()), \
SLOT(slotInsertHoldFramesCustom())); +
+        action = m_d->actionMan->createAction("remove_hold_frame");
+        connect(action, SIGNAL(triggered()), \
SLOT(slotRemoveHoldFrames())); +
+        action = m_d->actionMan->createAction("remove_n_hold_frames");
+        connect(action, SIGNAL(triggered()), \
SLOT(slotRemoveHoldFramesCustom()));  }
 }
 
@@ -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 = false;
+
+    // TODO: save previous entered value till the end of the session
+    const int count = 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 = false;
+
+    // TODO: save previous entered value till the end of the session
+    const int count = 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/dockers/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 = 1);
+    void slotInsertHoldFramesCustom();
+
+    void slotRemoveHoldFrames(int count = 1);
+    void slotRemoveHoldFramesCustom();
+
     void slotReselectCurrentIndex();
 
     void slotUpdateInfiniteFramesCount();


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic