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

Dmitry Kazakov null at kde.org
Fri Aug 14 21:01:54 BST 2020


Git commit bac3cdd85570404f17c156d79a4200f9932b4d92 by Dmitry Kazakov.
Committed on 14/08/2020 at 19:54.
Pushed by dkazakov into branch 'krita/4.3'.

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/bac3cdd85570404f17c156d79a4200f9932b4d92

diff --git a/libs/image/kis_layer_utils.cpp b/libs/image/kis_layer_utils.cpp
index efb07204c3..8c5439ab7e 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 7b8321df5a..a3935d6f2b 100644
--- a/libs/ui/kis_layer_manager.cc
+++ b/libs/ui/kis_layer_manager.cc
@@ -798,6 +798,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