[graphics/krita] libs: Fix flatten/merge down/merge multiple action handle locked groups correctly

Dmitry Kazakov null at kde.org
Fri Aug 14 21:02:18 BST 2020


Git commit fe9741d27cb87a59b3add558489ce5b407a35d9a by Dmitry Kazakov.
Committed on 14/08/2020 at 20:02.
Pushed by dkazakov into branch 'master'.

Fix flatten/merge down/merge multiple action handle locked groups correctly

This patch extends the already existing strategy, which was previously
used for merge down operation only:

1) If there is at least one unlocked layer in the selection, then the
   merge will happen.
2) The merge result will be written into nearest unlocked parent
3) All unlocked layers of the selection will be removed
4) All locked layers will be kept in the layers stack, but switched
   into *invisible* state.

CCBUG:406697
CC:kimageshop at kde.org

M  +85   -20   libs/image/kis_layer_utils.cpp
M  +18   -0    libs/ui/kis_layer_manager.cc

https://invent.kde.org/graphics/krita/commit/fe9741d27cb87a59b3add558489ce5b407a35d9a

diff --git a/libs/image/kis_layer_utils.cpp b/libs/image/kis_layer_utils.cpp
index dd700d7562..25c59b43ca 100644
--- a/libs/image/kis_layer_utils.cpp
+++ b/libs/image/kis_layer_utils.cpp
@@ -692,6 +692,52 @@ namespace KisLayerUtils {
     };
 
 
+    void splitNonRemovableNodes(KisNodeList &nodesToRemove, KisNodeList &_nodesToHide)
+    {
+        QSet<KisNodeSP> nodesToHide;
+        QSet<KisNodeSP> extraNodesToRemove;
+
+        for (auto it = nodesToRemove.begin(); it != nodesToRemove.end(); ++it) {
+            KisNodeSP root = *it;
+            KIS_SAFE_ASSERT_RECOVER_NOOP(root->visible());
+
+            if (!root->isEditable(false)) {
+                nodesToHide.insert(root);
+            } else {
+                bool rootNeedsCarefulRemoval = false;
+
+                recursiveApplyNodes(root,
+                                    [root, &nodesToHide, &rootNeedsCarefulRemoval] (KisNodeSP node) {
+                                        if (!node->isEditable(false)) {
+                                            while (node != root) {
+                                                nodesToHide.insert(node);
+                                                node = node->parent();
+                                                KIS_SAFE_ASSERT_RECOVER_BREAK(node);
+                                            }
+                                            nodesToHide.insert(root);
+                                            rootNeedsCarefulRemoval = true;
+                                        }
+                                    });
+
+                if (rootNeedsCarefulRemoval) {
+                    recursiveApplyNodes(root,
+                                        [&extraNodesToRemove] (KisNodeSP node) {
+                                            extraNodesToRemove.insert(node);
+                                        });
+                }
+            }
+        }
+
+        nodesToRemove += extraNodesToRemove.toList();
+
+        KritaUtils::filterContainer<KisNodeList>(nodesToRemove,
+                                                 [nodesToHide](KisNodeSP node) {
+                                                     return !nodesToHide.contains(node);
+                                                 });
+
+        _nodesToHide = nodesToHide.toList();
+    }
+
     struct CleanUpNodes : private RemoveNodeHelper, public KisCommandUtils::AggregateCommand {
         CleanUpNodes(MergeDownInfoBaseSP info, KisNodeSP putAfter)
             : m_info(info), m_putAfter(putAfter) {}
@@ -792,16 +838,14 @@ namespace KisLayerUtils {
                 }
 
                 KisNodeList safeNodesToDelete = m_info->allSrcNodes();
-                for (KisNodeList::iterator it = safeNodesToDelete.begin(); it != safeNodesToDelete.end(); ++it) {
-                    KisNodeSP node = *it;
-                    if (node->userLocked() && node->visible()) {
-                        addCommand(new KisImageChangeVisibilityCommand(false, node));
-                    }
+                KisNodeList safeNodesToHide;
+
+                splitNonRemovableNodes(safeNodesToDelete, safeNodesToHide);
+
+                Q_FOREACH(KisNodeSP node, safeNodesToHide) {
+                    addCommand(new KisImageChangeVisibilityCommand(false, node));
                 }
 
-                KritaUtils::filterContainer<KisNodeList>(safeNodesToDelete, [](KisNodeSP node) {
-                  return !node->userLocked();
-                });
                 safeRemoveMultipleNodes(safeNodesToDelete, m_info->image);
             }
 
@@ -1312,25 +1356,46 @@ namespace KisLayerUtils {
         emitSignals << ModifiedSignal;
         emitSignals << ComplexNodeReselectionSignal(KisNodeSP(), KisNodeList(), KisNodeSP(), mergedNodes);
 
-        KisProcessingApplicator applicator(image, 0,
-                                           KisProcessingApplicator::NONE,
-                                           emitSignals,
-                                           actionName);
 
 
         KisNodeList originalNodes = mergedNodes;
         KisNodeList invisibleNodes;
         mergedNodes = filterInvisibleNodes(originalNodes, &invisibleNodes, &putAfter);
 
-        if (!invisibleNodes.isEmpty() && !mergedNodes.isEmpty()) {
-            /* If the putAfter node is invisible,
-             * we should instead pick one of the nodes
-             * to be merged to avoid a null putAfter.
-             */
-            if (!putAfter->visible()){
-                putAfter = mergedNodes.first();
-            }
+        if (mergedNodes.isEmpty()) return;
+
+        /* If the putAfter node is invisible,
+         * we should instead pick one of the nodes
+         * to be merged to avoid a null putAfter.
+         */
+        if (!putAfter->visible()){
+            putAfter = mergedNodes.first();
+        }
+
+        // make sure we don't add the new layer into a locked group
+        KIS_SAFE_ASSERT_RECOVER_RETURN(putAfter->parent());
+        while (putAfter->parent() && !putAfter->parent()->isEditable()) {
+            putAfter = putAfter->parent();
+        }
+
+        /**
+         * We have reached the root of the layer hierarchy and didn't manage
+         * to find a node that was editable enough for putting our merged
+         * result into it. That whouldn't happen in normal circumstances,
+         * unless the user chose to make the root layer visible and lock
+         * it manually.
+         */
+        if (!putAfter->parent()) {
+            return;
+        }
+
+        KisProcessingApplicator applicator(image, 0,
+                                           KisProcessingApplicator::NONE,
+                                           emitSignals,
+                                           actionName);
+
 
+        if (!invisibleNodes.isEmpty()) {
             applicator.applyCommand(
                 new SimpleRemoveLayers(invisibleNodes,
                                        image),
diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc
index 19d804dae7..dfa0b8c8c6 100644
--- a/libs/ui/kis_layer_manager.cc
+++ b/libs/ui/kis_layer_manager.cc
@@ -804,6 +804,24 @@ void KisLayerManager::mergeLayer()
     if (!m_view->blockUntilOperationsFinished(image)) return;
 
     QList<KisNodeSP> selectedNodes = m_view->nodeManager()->selectedNodes();
+
+    // check if all the layers are a part of a locked group
+    bool hasEditableLayer = false;
+    Q_FOREACH (KisNodeSP node, selectedNodes) {
+        if (node->isEditable()) {
+            hasEditableLayer = true;
+            break;
+        }
+    }
+
+    if (!hasEditableLayer) {
+        m_view->showFloatingMessage(
+                    i18nc("floating message in layer manager",
+                          "Layer is locked "),
+                    QIcon(), 2000, KisFloatingMessage::Low);
+        return;
+    }
+
     if (selectedNodes.size() > 1) {
         image->mergeMultipleLayers(selectedNodes, m_view->activeNode());
     }



More information about the kimageshop mailing list