[krita] krita: Implement Move Up/Down actions for multiple layer selections
Dmitry Kazakov
dimula73 at gmail.com
Mon Dec 28 17:17:44 UTC 2015
Git commit 8170a9252f3e9cfee38c7ef98779379a65015442 by Dmitry Kazakov.
Committed on 28/12/2015 at 17:17.
Pushed by dkazakov into branch 'master'.
Implement Move Up/Down actions for multiple layer selections
Now one cane move up/down layers and:
1) The moves are compressed with the delay of 1 sec
2) The layers are automatically enter/leave groups which stay on their way.
3) The selection of the layers is kept unchanged after the move
CC:kimageshop at kde.org
M +3 -0 krita/image/kis_layer_utils.h
M +8 -1 krita/image/kis_node.cpp
M +2 -1 krita/image/kis_types.h
M +11 -0 krita/image/krita_utils.h
M +51 -28 krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
M +3 -0 krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h
M +189 -27 krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.cpp
M +5 -3 krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h
M +1 -1 krita/plugins/extensions/dockers/defaultdockers/tests/kis_node_juggler_compressed_test.cpp
M +2 -7 krita/sketch/models/LayerModel.cpp
M +1 -0 krita/ui/CMakeLists.txt
M +10 -5 krita/ui/KisNodeView.cpp
M +33 -10 krita/ui/kis_node_manager.cpp
M +14 -8 krita/ui/kis_node_manager.h
M +23 -7 krita/ui/kis_node_model.cpp
M +2 -1 krita/ui/kis_node_model.h
A +47 -0 krita/ui/kis_node_selection_adapter.cpp [License: GPL (v2+)]
C +11 -28 krita/ui/kis_node_selection_adapter.h [from: krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h - 054% similarity]
M +2 -2 krita/ui/tests/kis_model_index_converter_test.cpp
M +5 -5 krita/ui/tests/kis_node_model_test.cpp
http://commits.kde.org/krita/8170a9252f3e9cfee38c7ef98779379a65015442
diff --git a/krita/image/kis_layer_utils.h b/krita/image/kis_layer_utils.h
index 4463ab3..384d3fa 100644
--- a/krita/image/kis_layer_utils.h
+++ b/krita/image/kis_layer_utils.h
@@ -30,6 +30,9 @@ namespace KisMetaData
namespace KisLayerUtils
{
+ KRITAIMAGE_EXPORT void sortMergableNodes(KisNodeSP root, QList<KisNodeSP> &inputNodes, QList<KisNodeSP> &outputNodes);
+ KRITAIMAGE_EXPORT void filterMergableNodes(QList<KisNodeSP> &nodes);
+
KRITAIMAGE_EXPORT void mergeDown(KisImageSP image, KisLayerSP layer, const KisMetaData::MergeStrategy* strategy);
KRITAIMAGE_EXPORT QSet<int> fetchLayerFrames(KisNodeSP node);
diff --git a/krita/image/kis_node.cpp b/krita/image/kis_node.cpp
index ec4fd96..dfdb51b 100644
--- a/krita/image/kis_node.cpp
+++ b/krita/image/kis_node.cpp
@@ -56,7 +56,14 @@ struct KisNodeSPStaticRegistrar {
qRegisterMetaType<KisNodeSP>("KisNodeSP");
}
};
-static KisNodeSPStaticRegistrar __registrar;
+static KisNodeSPStaticRegistrar __registrar1;
+
+struct KisNodeListStaticRegistrar {
+ KisNodeListStaticRegistrar() {
+ qRegisterMetaType<KisNodeList>("KisNodeList");
+ }
+};
+static KisNodeListStaticRegistrar __registrar2;
/**
diff --git a/krita/image/kis_types.h b/krita/image/kis_types.h
index 1cdcb7a..1b1f977 100644
--- a/krita/image/kis_types.h
+++ b/krita/image/kis_types.h
@@ -222,7 +222,8 @@ typedef KisSharedPtr<KisProcessingVisitor> KisProcessingVisitorSP;
class KUndo2Command;
typedef QSharedPointer<KUndo2Command> KUndo2CommandSP;
-typedef QSharedPointer<QList <KisNodeSP> > KisNodeListSP;
+typedef QList<KisNodeSP> KisNodeList;
+typedef QSharedPointer<KisNodeList> KisNodeListSP;
class KisStroke;
typedef QSharedPointer<KisStroke> KisStrokeSP;
diff --git a/krita/image/krita_utils.h b/krita/image/krita_utils.h
index 567bc77..6d54fe4 100644
--- a/krita/image/krita_utils.h
+++ b/krita/image/krita_utils.h
@@ -69,6 +69,17 @@ namespace KritaUtils
QString KRITAIMAGE_EXPORT toLocalizedOnOff(bool value);
KisNodeSP KRITAIMAGE_EXPORT nearestNodeAfterRemoval(KisNodeSP node);
+
+ template <class T>
+ bool compareListsUnordered(const QList<T> &a, const QList<T> &b) {
+ if (a.size() != b.size()) return false;
+
+ Q_FOREACH(const T &t, a) {
+ if (!b.contains(t)) return false;
+ }
+
+ return true;
+ }
}
#endif /* __KRITA_UTILS_H */
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
index 31650a1..e0964f8 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
@@ -76,6 +76,7 @@
#include "kis_config.h"
#include "KisView.h"
#include "kis_node_juggler_compressed.h"
+#include "krita_utils.h"
#include "ui_wdglayerbox.h"
@@ -122,6 +123,7 @@ KisLayerBox::KisLayerBox()
: QDockWidget(i18n("Layers"))
, m_canvas(0)
, m_wdgLayerBox(new Ui_WdgLayerBox)
+ , m_thumbnailCompressor(500, KisSignalCompressor::FIRST_INACTIVE)
{
KisConfig cfg;
@@ -131,11 +133,6 @@ KisLayerBox::KisLayerBox()
m_wdgLayerBox->setupUi(mainWidget);
- m_wdgLayerBox->listLayers->setDefaultDropAction(Qt::MoveAction);
- m_wdgLayerBox->listLayers->setSelectionMode(QAbstractItemView::ExtendedSelection);
- m_wdgLayerBox->listLayers->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
- m_wdgLayerBox->listLayers->setSelectionBehavior(QAbstractItemView::SelectRows);
-
connect(m_wdgLayerBox->listLayers,
SIGNAL(contextMenuRequested(const QPoint&, const QModelIndex&)),
this, SLOT(slotContextMenuRequested(const QPoint&, const QModelIndex&)));
@@ -269,6 +266,8 @@ KisLayerBox::KisLayerBox()
m_wdgLayerBox->listLayers->setModel(m_nodeModel);
setEnabled(false);
+
+ connect(&m_thumbnailCompressor, SIGNAL(timeout()), SLOT(updateThumbnail()));
}
KisLayerBox::~KisLayerBox()
@@ -305,7 +304,6 @@ void KisLayerBox::setMainWindow(KisViewManager* kisview)
{
m_nodeManager = kisview->nodeManager();
-
Q_FOREACH (KisAction *action, m_actions) {
kisview->actionManager()->
addAction(action->objectName(),
@@ -325,13 +323,13 @@ void KisLayerBox::setCanvas(KoCanvasBase *canvas)
if (m_canvas) {
m_canvas->disconnectCanvasObserver(this);
- m_nodeModel->setDummiesFacade(0, 0, 0);
+ m_nodeModel->setDummiesFacade(0, 0, 0, 0);
disconnect(m_image, 0, this, 0);
disconnect(m_nodeManager, 0, this, 0);
disconnect(m_nodeModel, 0, m_nodeManager, 0);
disconnect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(updateUI()));
- m_nodeManager->setSelectedNodes(QList<KisNodeSP>());
+ m_nodeManager->slotSetSelectedNodes(KisNodeList());
}
m_canvas = dynamic_cast<KisCanvas2*>(canvas);
@@ -339,14 +337,14 @@ void KisLayerBox::setCanvas(KoCanvasBase *canvas)
if (m_canvas) {
m_image = m_canvas->image();
- connect(m_image, SIGNAL(sigImageUpdated(QRect)), SLOT(updateThumbnail()));
+ connect(m_image, SIGNAL(sigImageUpdated(QRect)), &m_thumbnailCompressor, SLOT(start()));
KisDocument* doc = static_cast<KisDocument*>(m_canvas->imageView()->document());
KisShapeController *kritaShapeController =
dynamic_cast<KisShapeController*>(doc->shapeController());
KisDummiesFacadeBase *kritaDummiesFacade =
static_cast<KisDummiesFacadeBase*>(kritaShapeController);
- m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController);
+ m_nodeModel->setDummiesFacade(kritaDummiesFacade, m_image, kritaShapeController, m_nodeManager->nodeSelectionAdapter());
connect(m_image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
connect(m_image, SIGNAL(sigNodeCollapsedChanged()), SLOT(slotNodeCollapsedChanged()));
@@ -363,11 +361,9 @@ void KisLayerBox::setCanvas(KoCanvasBase *canvas)
connect(m_nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)),
this, SLOT(setCurrentNode(KisNodeSP)));
- // Connection KisLayerBox -> KisNodeManager
- // The order of these connections is important! See comment in the ctor
- connect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)),
- m_nodeManager, SLOT(slotUiActivatedNode(KisNodeSP)));
- connect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), SLOT(updateUI()));
+ connect(m_nodeManager,
+ SIGNAL(sigUiNeedChangeSelectedNodes(const QList<KisNodeSP> &)),
+ SLOT(slotNodeManagerChangedSelection(const QList<KisNodeSP> &)));
// Connection KisLayerBox -> KisNodeManager (isolate layer)
connect(m_nodeModel, SIGNAL(toggleIsolateActiveNode()),
@@ -407,12 +403,12 @@ void KisLayerBox::unsetCanvas()
m_newLayerMenu->clear();
}
- m_nodeModel->setDummiesFacade(0, 0, 0);
+ m_nodeModel->setDummiesFacade(0, 0, 0, 0);
disconnect(m_image, 0, this, 0);
disconnect(m_nodeManager, 0, this, 0);
disconnect(m_nodeModel, 0, m_nodeManager, 0);
disconnect(m_nodeModel, SIGNAL(nodeActivated(KisNodeSP)), this, SLOT(updateUI()));
- m_nodeManager->setSelectedNodes(QList<KisNodeSP>());
+ m_nodeManager->slotSetSelectedNodes(KisNodeList());
m_canvas = 0;
}
@@ -478,7 +474,7 @@ void KisLayerBox::setCurrentNode(KisNodeSP node)
{
QModelIndex index = node ? m_nodeModel->indexFromNode(node) : QModelIndex();
- m_wdgLayerBox->listLayers->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
+ m_nodeModel->setData(index, true, KisNodeModel::ActiveRole);
updateUI();
}
@@ -606,11 +602,11 @@ void KisLayerBox::slotRaiseClicked()
if (!m_canvas) return;
if (!m_nodeJuggler) {
- m_nodeJuggler = new KisNodeJugglerCompressed(kundo2_i18n("Move Nodes"), m_image, 3000);
+ m_nodeJuggler = new KisNodeJugglerCompressed(kundo2_i18n("Move Nodes"), m_image, m_nodeManager, 1000);
m_nodeJuggler->setAutoDelete(true);
}
- m_nodeJuggler->raiseNode(m_nodeManager->activeNode());
+ m_nodeJuggler->raiseNode(m_nodeManager->selectedNodes());
}
void KisLayerBox::slotLowerClicked()
@@ -618,11 +614,11 @@ void KisLayerBox::slotLowerClicked()
if (!m_canvas) return;
if (!m_nodeJuggler) {
- m_nodeJuggler = new KisNodeJugglerCompressed(kundo2_i18n("Move Nodes"), m_image, 3000);
+ m_nodeJuggler = new KisNodeJugglerCompressed(kundo2_i18n("Move Nodes"), m_image, m_nodeManager, 1000);
m_nodeJuggler->setAutoDelete(true);
}
- m_nodeJuggler->lowerNode(m_nodeManager->activeNode());
+ m_nodeJuggler->lowerNode(m_nodeManager->selectedNodes());
}
void KisLayerBox::slotPropertiesClicked()
@@ -754,25 +750,52 @@ void KisLayerBox::selectionChanged(const QModelIndexList selection)
{
if (!m_nodeManager) return;
+ /**
+ * When the user clears the extended selection by clicking on the
+ * empty area of the docker, the selection should be reset on to
+ * the active layer, which might be even unselected(!).
+ */
if (selection.isEmpty() && m_nodeManager->activeNode()) {
+ QModelIndex selectedIndex =
+ m_nodeModel->indexFromNode(m_nodeManager->activeNode());
+
m_wdgLayerBox->listLayers->selectionModel()->
- setCurrentIndex(
- m_nodeModel->indexFromNode(m_nodeManager->activeNode()),
- QItemSelectionModel::ClearAndSelect);
+ setCurrentIndex(selectedIndex, QItemSelectionModel::ClearAndSelect);
return;
}
-
QList<KisNodeSP> selectedNodes;
Q_FOREACH (const QModelIndex &idx, selection) {
selectedNodes << m_nodeModel->nodeFromIndex(idx);
}
-
- m_nodeManager->setSelectedNodes(selectedNodes);
+ m_nodeManager->slotSetSelectedNodes(selectedNodes);
updateUI();
}
+void KisLayerBox::slotNodeManagerChangedSelection(const KisNodeList &nodes)
+{
+ if (!m_nodeManager) return;
+
+ QModelIndexList newSelection;
+ Q_FOREACH(KisNodeSP node, nodes) {
+ newSelection << m_nodeModel->indexFromNode(node);
+ }
+
+ QItemSelectionModel *model = m_wdgLayerBox->listLayers->selectionModel();
+
+ if (KritaUtils::compareListsUnordered(newSelection, model->selectedIndexes())) {
+ return;
+ }
+
+ QItemSelection selection;
+ Q_FOREACH(const QModelIndex &idx, newSelection) {
+ selection.select(idx, idx);
+ }
+
+ model->select(selection, QItemSelectionModel::ClearAndSelect);
+}
+
void KisLayerBox::updateThumbnail()
{
m_wdgLayerBox->listLayers->updateNode(m_wdgLayerBox->listLayers->currentIndex());
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h
index d921216..5be6448 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.h
@@ -39,6 +39,7 @@
#include "kis_action.h"
#include "KisViewManager.h"
#include "kis_mainwindow_observer.h"
+#include "kis_signal_compressor.h"
class QModelIndex;
@@ -112,6 +113,7 @@ private Q_SLOTS:
void slotEditGlobalSelection(bool showSelections);
void selectionChanged(const QModelIndexList selection);
+ void slotNodeManagerChangedSelection(const QList<KisNodeSP> &nodes);
void updateThumbnail();
@@ -137,6 +139,7 @@ private:
KisAction* m_propertiesAction;
KisAction* m_selectOpaque;
QPointer<KisNodeJugglerCompressed> m_nodeJuggler;
+ KisSignalCompressor m_thumbnailCompressor;
};
class KisLayerBoxFactory : public KoDockFactoryBase
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.cpp b/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.cpp
index 0b1feed..1f45ace 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.cpp
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.cpp
@@ -28,8 +28,22 @@
#include "commands/kis_image_layer_move_command.h"
#include "kis_signal_compressor.h"
#include "kis_command_utils.h"
+#include "kis_layer_utils.h"
+#include "kis_node_manager.h"
+/**
+ * A special structure that stores information about a node that was
+ * moved. The purpose of the object is twofold:
+ *
+ * 1) When the reordering stroke is already started than the
+ * parent and sibling nodes may be not consistent anymore. So
+ * we store it separately.
+ *
+ * 2) This objects allows merging (compressing) multiple moves of
+ * a layer into a signle action. This behavior is implemented
+ * in tryMerge() method.
+ */
struct MoveNodeStruct {
MoveNodeStruct(KisImageSP _image, KisNodeSP _node, KisNodeSP _parent, KisNodeSP _above)
: image(_image),
@@ -93,13 +107,26 @@ struct MoveNodeStruct {
typedef QSharedPointer<MoveNodeStruct> MoveNodeStructSP;
typedef QHash<KisNodeSP, MoveNodeStructSP> MovedNodesHash;
-struct BatchMoveUpdateData {
+
+/**
+ * All the commands executed bythe stroke system are running in the
+ * background asynchronously. But, at the same time, they emit updates
+ * in parallel to the ones emitted by the juggler. Therefore, the
+ * juggler and all its commands should share some data: which updates
+ * have been requested, but not yet dispatched (m_movedNodesInitial),
+ * and what updates have already been processed and executed
+ * (m_movedNodesUpdated). This object is shared via a shared pointer
+ * and guarantees safe (including thread-safe) access to the shared
+ * data.
+ */
+class BatchMoveUpdateData {
MovedNodesHash m_movedNodesInitial;
MovedNodesHash m_movedNodesUpdated;
- bool m_jugglerAlive;
QMutex m_mutex;
+private:
+
static void addToHashLazy(MovedNodesHash *hash, MoveNodeStructSP moveStruct) {
if (hash->contains(moveStruct->node)) {
bool result = hash->value(moveStruct->node)->tryMerge(*moveStruct);
@@ -109,6 +136,8 @@ struct BatchMoveUpdateData {
}
}
+public:
+
void processUnhandledUpdates() {
QMutexLocker l(&m_mutex);
@@ -131,6 +160,8 @@ struct BatchMoveUpdateData {
}
void emitFinalUpdates(bool undo) {
+ QMutexLocker l(&m_mutex);
+
if (m_movedNodesUpdated.isEmpty()) return;
MovedNodesHash::const_iterator it = m_movedNodesUpdated.constBegin();
@@ -148,6 +179,10 @@ struct BatchMoveUpdateData {
typedef QSharedPointer<BatchMoveUpdateData> BatchMoveUpdateDataSP;
+/**
+ * A command that emits a update signals on the compressed move undo
+ * or redo.
+ */
class UpdateMovedNodesCommand : public KisCommandUtils::FlipFlopCommand
{
public:
@@ -158,36 +193,131 @@ public:
}
void end() {
- if (isFirstRedo()) {
+ if (isFinalizing() && isFirstRedo()) {
+ /**
+ * When doing the first redo() some of the updates might
+ * have already been executed by the juggler itself, so we
+ * should process'unhandled' updates only
+ */
m_updateData->processUnhandledUpdates();
} else {
+ /**
+ * When being executed by real undo/redo operations, we
+ * should emit all the update signals. Noone else will do
+ * that for us (juggler, which did it in the previous
+ * case, might have already died).
+ */
m_updateData->emitFinalUpdates(isFinalizing());
}
}
private:
BatchMoveUpdateDataSP m_updateData;
- bool m_firstRun;
};
+/**
+ * A special QObject-based proxy object that connects to the node
+ * manager and allows the command to emit signals to change
+ * active/selected nodes from another (non-gui) thread.
+ */
+class SelectionSignalsProxy : public QObject {
+ Q_OBJECT
+public:
+ SelectionSignalsProxy(KisNodeManager *nodeManager, QObject *parent = 0)
+ : QObject(parent)
+ {
+ connect(this, SIGNAL(sigActivateNode(KisNodeSP)), nodeManager, SLOT(slotNonUiActivatedNode(KisNodeSP)));
+ connect(this, SIGNAL(sigSetSelectedNodes(const KisNodeList&)), nodeManager, SLOT(slotSetSelectedNodes(const KisNodeList&)));
+ }
+
+ void changeLayersSelection(KisNodeSP activeNode, const KisNodeList &selectedNodes) {
+ emit sigActivateNode(activeNode);
+ emit sigSetSelectedNodes(selectedNodes);
+ }
+
+Q_SIGNALS:
+ void sigActivateNode(KisNodeSP node);
+ void sigSetSelectedNodes(const KisNodeList &nodes);
+};
+
+/**
+ * A command to keep correct set of selected/active nodes thoroughout
+ * the action.
+ */
+class KeepNodesSelectedCommand : public KisCommandUtils::FlipFlopCommand
+{
+public:
+ KeepNodesSelectedCommand(const KisNodeList &selectedBefore,
+ const KisNodeList &selectedAfter,
+ KisNodeSP activeBefore,
+ KisNodeSP activeAfter,
+ KisNodeManager *nodeManager,
+ bool finalize, KUndo2Command *parent = 0)
+ : FlipFlopCommand(finalize, parent),
+ m_selectedBefore(selectedBefore),
+ m_selectedAfter(selectedAfter),
+ m_activeBefore(activeBefore),
+ m_activeAfter(activeAfter),
+ m_nodeManager(nodeManager),
+ m_signalProxy(new SelectionSignalsProxy(nodeManager))
+ {
+ }
+
+ void end() {
+ if (isFinalizing()) {
+ m_signalProxy->changeLayersSelection(m_activeAfter, m_selectedAfter);
+ } else {
+ m_signalProxy->changeLayersSelection(m_activeBefore, m_selectedBefore);
+ }
+ }
+
+private:
+ KisNodeList m_selectedBefore;
+ KisNodeList m_selectedAfter;
+ KisNodeSP m_activeBefore;
+ KisNodeSP m_activeAfter;
+ KisNodeManager *m_nodeManager;
+ QScopedPointer<SelectionSignalsProxy> m_signalProxy;
+};
+
+/**
+ * A generalized command to muve up/down a set of layer
+ */
struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
LowerRaiseLayer(BatchMoveUpdateDataSP updateData,
- KisImageSP image,
- KisNodeSP node,
- bool lower)
+ KisNodeManager *nodeManager,
+ KisImageSP image,
+ const KisNodeList &nodes,
+ KisNodeSP activeNode,
+ bool lower)
: m_updateData(updateData),
+ m_nodeManager(nodeManager),
m_image(image),
- m_node(node),
+ m_nodes(nodes),
+ m_activeNode(activeNode),
m_lower (lower) {}
+ KisNodeList sortAndFilterNodes(const KisNodeList &nodes) {
+ KisNodeList filteredNodes = nodes;
+ KisNodeList sortedNodes;
+
+ KisLayerUtils::filterMergableNodes(filteredNodes);
+ KisLayerUtils::sortMergableNodes(m_image->root(), filteredNodes, sortedNodes);
+
+ return sortedNodes;
+ }
+
void populateChildCommands() {
- KisNodeSP parent = m_node->parent();
+ KisNodeList sortedNodes = sortAndFilterNodes(m_nodes);
+ KisNodeSP headNode = m_lower ? sortedNodes.first() : sortedNodes.last();
+
+ KisNodeSP parent = headNode->parent();
KisNodeSP grandParent = parent ? parent->parent() : 0;
KisNodeSP newAbove;
KisNodeSP newParent;
if (m_lower) {
- KisNodeSP prevNode = m_node->prevSibling();
+ KisNodeSP prevNode = headNode->prevSibling();
if (prevNode) {
if (prevNode->inherits("KisGroupLayer") &&
@@ -200,13 +330,13 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
newParent = parent;
}
} else if (grandParent &&
- !m_node->inherits("KisMask")) {
+ !headNode->inherits("KisMask")) { // TODO
newAbove = parent->prevSibling();
newParent = grandParent;
}
} else {
- KisNodeSP nextNode = m_node->nextSibling();
+ KisNodeSP nextNode = headNode->nextSibling();
if (nextNode) {
if (nextNode->inherits("KisGroupLayer") &&
@@ -219,7 +349,7 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
newParent = parent;
}
} else if (grandParent &&
- !m_node->inherits("KisMask")) {
+ !headNode->inherits("KisMask")) { // TODO
newAbove = parent;
newParent = grandParent;
@@ -228,23 +358,37 @@ struct LowerRaiseLayer : public KisCommandUtils::AggregateCommand {
if (!newParent) return;
- MoveNodeStructSP moveStruct = toQShared(new MoveNodeStruct(m_image, m_node, newParent, newAbove));
- addCommand(new KisImageLayerMoveCommand(m_image, m_node, newParent, newAbove, false));
- m_updateData->addInitialUpdate(moveStruct);
+ addCommand(new KeepNodesSelectedCommand(sortedNodes, sortedNodes,
+ m_activeNode, m_activeNode,
+ m_nodeManager, false));
+
+ KisNodeSP currentAbove = newAbove;
+ Q_FOREACH (KisNodeSP node, sortedNodes) {
+ MoveNodeStructSP moveStruct = toQShared(new MoveNodeStruct(m_image, node, newParent, currentAbove));
+ addCommand(new KisImageLayerMoveCommand(m_image, node, newParent, currentAbove, false));
+ m_updateData->addInitialUpdate(moveStruct);
+ currentAbove = node;
+ }
+
+ addCommand(new KeepNodesSelectedCommand(sortedNodes, sortedNodes,
+ m_activeNode, m_activeNode,
+ m_nodeManager, true));
}
private:
BatchMoveUpdateDataSP m_updateData;
-
+ KisNodeManager *m_nodeManager;
KisImageSP m_image;
- KisNodeSP m_node;
+ KisNodeList m_nodes;
+ KisNodeSP m_activeNode;
bool m_lower;
};
struct KisNodeJugglerCompressed::Private
{
- Private(KisImageSP _image, int _timeout)
+ Private(KisImageSP _image, KisNodeManager *_nodeManager, int _timeout)
: image(_image),
+ nodeManager(_nodeManager),
compressor(_timeout, KisSignalCompressor::POSTPONE),
selfDestructionCompressor(3 * _timeout, KisSignalCompressor::POSTPONE),
updateData(new BatchMoveUpdateData),
@@ -253,6 +397,7 @@ struct KisNodeJugglerCompressed::Private
{}
KisImageSP image;
+ KisNodeManager *nodeManager;
QScopedPointer<KisProcessingApplicator> applicator;
KisSignalCompressor compressor;
@@ -264,8 +409,8 @@ struct KisNodeJugglerCompressed::Private
bool isStarted;
};
-KisNodeJugglerCompressed::KisNodeJugglerCompressed(const KUndo2MagicString &actionName, KisImageSP image, int timeout)
- : m_d(new Private(image, timeout))
+KisNodeJugglerCompressed::KisNodeJugglerCompressed(const KUndo2MagicString &actionName, KisImageSP image, KisNodeManager *nodeManager, int timeout)
+ : m_d(new Private(image, nodeManager, timeout))
{
connect(m_d->image, SIGNAL(sigStrokeCancellationRequested()), SLOT(slotEndStrokeRequested()));
connect(m_d->image, SIGNAL(sigStrokeEndRequestedActiveNodeFiltered()), SLOT(slotEndStrokeRequested()));
@@ -281,7 +426,7 @@ KisNodeJugglerCompressed::KisNodeJugglerCompressed(const KUndo2MagicString &acti
connect(&m_d->compressor, SIGNAL(timeout()), SLOT(slotUpdateTimeout()));
m_d->applicator->applyCommand(
- new UpdateMovedNodesCommand(m_d->updateData, false));
+ new UpdateMovedNodesCommand(m_d->updateData, false));
m_d->isStarted = true;
}
@@ -289,18 +434,33 @@ KisNodeJugglerCompressed::~KisNodeJugglerCompressed()
{
}
-void KisNodeJugglerCompressed::lowerNode(KisNodeSP node)
+KisNodeList KisNodeJugglerCompressed::sortAndFilterNodes(const KisNodeList &nodes)
+{
+ KisNodeList filteredNodes = nodes;
+ KisNodeList sortedNodes;
+
+ KisLayerUtils::filterMergableNodes(filteredNodes);
+ KisLayerUtils::sortMergableNodes(m_d->image->root(), filteredNodes, sortedNodes);
+
+ return sortedNodes;
+}
+
+void KisNodeJugglerCompressed::lowerNode(const KisNodeList &nodes)
{
m_d->applicator->applyCommand(
- new LowerRaiseLayer(m_d->updateData, m_d->image, node, true));
+ new LowerRaiseLayer(m_d->updateData, m_d->nodeManager,
+ m_d->image,
+ nodes, m_d->nodeManager->activeNode(), true));
startTimers();
}
-void KisNodeJugglerCompressed::raiseNode(KisNodeSP node)
+void KisNodeJugglerCompressed::raiseNode(const KisNodeList &nodes)
{
m_d->applicator->applyCommand(
- new LowerRaiseLayer(m_d->updateData, m_d->image, node, false));
+ new LowerRaiseLayer(m_d->updateData, m_d->nodeManager,
+ m_d->image,
+ nodes, m_d->nodeManager->activeNode(), false));
startTimers();
}
@@ -333,7 +493,7 @@ void KisNodeJugglerCompressed::end()
if (!m_d->isStarted) return;
m_d->applicator->applyCommand(
- new UpdateMovedNodesCommand(m_d->updateData, true));
+ new UpdateMovedNodesCommand(m_d->updateData, true));
m_d->applicator->end();
m_d->applicator.reset();
@@ -362,3 +522,5 @@ bool KisNodeJugglerCompressed::isEnded() const
{
return !m_d->isStarted;
}
+
+#include "kis_node_juggler_compressed.moc"
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h b/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h
index b4ecb2f..0dacbb3 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h
@@ -25,13 +25,14 @@
#include <kritadefaultdockers_export.h>
#include <kundo2command.h>
#include "kis_types.h"
+#include "kis_node_manager.h"
class KRITADEFAULTDOCKERS_EXPORT KisNodeJugglerCompressed : public QObject
{
Q_OBJECT
public:
- KisNodeJugglerCompressed(const KUndo2MagicString &actionName, KisImageSP image, int timeout);
+ KisNodeJugglerCompressed(const KUndo2MagicString &actionName, KisImageSP image, KisNodeManager *nodeManager, int timeout);
~KisNodeJugglerCompressed();
void moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP above);
@@ -39,8 +40,8 @@ public:
bool isEnded() const;
- void lowerNode(KisNodeSP node);
- void raiseNode(KisNodeSP node);
+ void lowerNode(const KisNodeList &nodes);
+ void raiseNode(const KisNodeList &nodes);
public Q_SLOTS:
void end();
@@ -51,6 +52,7 @@ private Q_SLOTS:
private:
void startTimers();
+ KisNodeList sortAndFilterNodes(const KisNodeList &nodes);
private:
struct Private;
diff --git a/krita/plugins/extensions/dockers/defaultdockers/tests/kis_node_juggler_compressed_test.cpp b/krita/plugins/extensions/dockers/defaultdockers/tests/kis_node_juggler_compressed_test.cpp
index 3202e6c..ddba0c4 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/tests/kis_node_juggler_compressed_test.cpp
+++ b/krita/plugins/extensions/dockers/defaultdockers/tests/kis_node_juggler_compressed_test.cpp
@@ -55,7 +55,7 @@ void KisNodeJugglerCompressedTest::testMove(int delayBeforeEnd)
TestUtil::ExternalImageChecker chk("node_juggler", "move_test");
chk.setMaxFailingPixels(0);
- KisNodeJugglerCompressed juggler(kundo2_i18n("Move Layer"), p->image, 600);
+ KisNodeJugglerCompressed juggler(kundo2_i18n("Move Layer"), p->image, 0, 600);
QVERIFY(chk.checkImage(p->image, "initial"));
juggler.moveNode(layer1, p->image->root(), layer2);
diff --git a/krita/sketch/models/LayerModel.cpp b/krita/sketch/models/LayerModel.cpp
index 2afb2b4..78b969e 100644
--- a/krita/sketch/models/LayerModel.cpp
+++ b/krita/sketch/models/LayerModel.cpp
@@ -261,7 +261,7 @@ void LayerModel::setView(QObject *newView)
d->layers.clear();
d->activeNode.clear();
d->canvas = 0;
- d->nodeModel->setDummiesFacade(0, 0, 0);
+ d->nodeModel->setDummiesFacade(0, 0, 0, 0);
}
@@ -283,7 +283,7 @@ void LayerModel::setView(QObject *newView)
KisDummiesFacadeBase *kritaDummiesFacade = dynamic_cast<KisDummiesFacadeBase*>(d->canvas->imageView()->document()->shapeController());
KisShapeController *shapeController = dynamic_cast<KisShapeController*>(d->canvas->imageView()->document()->shapeController());
- d->nodeModel->setDummiesFacade(kritaDummiesFacade, d->image, shapeController);
+ d->nodeModel->setDummiesFacade(kritaDummiesFacade, d->image, shapeController, d->nodeManager->nodeSelectionAdapter());
connect(d->image, SIGNAL(sigAboutToBeDeleted()), SLOT(notifyImageDeleted()));
connect(d->image, SIGNAL(sigNodeChanged(KisNodeSP)), SLOT(nodeChanged(KisNodeSP)));
@@ -296,11 +296,6 @@ void LayerModel::setView(QObject *newView)
// Connection KisNodeManager -> KisLayerBox
connect(d->nodeManager, SIGNAL(sigUiNeedChangeActiveNode(KisNodeSP)), this, SLOT(currentNodeChanged(KisNodeSP)));
- // Connection KisLayerBox -> KisNodeManager
- // The order of these connections is important! See comment in the ctor
- connect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), d->nodeManager, SLOT(slotUiActivatedNode(KisNodeSP)));
- connect(d->nodeModel, SIGNAL(nodeActivated(KisNodeSP)), SLOT(currentNodeChanged(KisNodeSP)));
-
// Node manipulation methods are forwarded to the node manager
connect(d->nodeModel, SIGNAL(requestAddNode(KisNodeSP, KisNodeSP, KisNodeSP)),
d->nodeManager, SLOT(addNodeDirect(KisNodeSP, KisNodeSP, KisNodeSP)));
diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index efc7910..df13163 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -106,6 +106,7 @@ set(kritaui_LIB_SRCS
kis_mimedata.cpp
kis_node_commands_adapter.cpp
kis_node_manager.cpp
+ kis_node_selection_adapter.cpp
kis_node_model.cpp
kis_model_index_converter_base.cpp
kis_model_index_converter.cpp
diff --git a/krita/ui/KisNodeView.cpp b/krita/ui/KisNodeView.cpp
index f49c3c0..30dbd3c 100644
--- a/krita/ui/KisNodeView.cpp
+++ b/krita/ui/KisNodeView.cpp
@@ -20,6 +20,7 @@
#include "KisNodePropertyAction_p.h"
#include "KisNodeDelegate.h"
#include "kis_node_model.h"
+#include "kis_signals_blocker.h"
#include <kconfig.h>
@@ -82,9 +83,11 @@ KisNodeView::KisNodeView(QWidget *parent)
, d(new Private(this))
{
setMouseTracking(true);
- setVerticalScrollMode(ScrollPerPixel);
- setSelectionMode(SingleSelection);
setSelectionBehavior(SelectItems);
+ setDefaultDropAction(Qt::MoveAction);
+ setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
+ setSelectionMode(QAbstractItemView::ExtendedSelection);
+
header()->hide();
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragDrop);
@@ -229,8 +232,11 @@ void KisNodeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bot
QTreeView::dataChanged(topLeft, bottomRight);
for (int x = topLeft.row(); x <= bottomRight.row(); ++x) {
for (int y = topLeft.column(); y <= bottomRight.column(); ++y) {
- if (topLeft.sibling(x, y).data(Model::ActiveRole).toBool()) {
- setCurrentIndex(topLeft.sibling(x, y));
+ QModelIndex index = topLeft.sibling(x, y);
+ if (index.data(Model::ActiveRole).toBool()) {
+ if (currentIndex() != index) {
+ setCurrentIndex(index);
+ }
return;
}
}
@@ -241,7 +247,6 @@ void KisNodeView::selectionChanged(const QItemSelection &selected, const QItemSe
{
QTreeView::selectionChanged(selected, deselected);
emit selectionChanged(selectedIndexes());
-
}
void KisNodeView::slotActionToggled(bool on, const QPersistentModelIndex &index, int num)
diff --git a/krita/ui/kis_node_manager.cpp b/krita/ui/kis_node_manager.cpp
index 803a503..b46ccf5 100644
--- a/krita/ui/kis_node_manager.cpp
+++ b/krita/ui/kis_node_manager.cpp
@@ -66,6 +66,7 @@
#include "kis_processing_applicator.h"
#include "kis_sequential_iterator.h"
#include "kis_transaction.h"
+#include "kis_node_selection_adapter.h"
#include "processing/kis_mirror_processing_visitor.h"
#include "KisView.h"
@@ -79,6 +80,7 @@ struct KisNodeManager::Private {
, layerManager(v)
, maskManager(v)
, commandsAdapter(v)
+ , nodeSelectionAdapter(new KisNodeSelectionAdapter(q))
{
}
@@ -88,8 +90,9 @@ struct KisNodeManager::Private {
KisLayerManager layerManager;
KisMaskManager maskManager;
KisNodeCommandsAdapter commandsAdapter;
+ QScopedPointer<KisNodeSelectionAdapter> nodeSelectionAdapter;
- QList<KisNodeSP> selectedNodes;
+ KisNodeList selectedNodes;
bool activateNodeImpl(KisNodeSP node);
@@ -493,8 +496,10 @@ void KisNodeManager::convertNode(const QString &nodeType)
}
}
-void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node)
+void KisNodeManager::slotSomethingActivatedNodeImpl(KisNodeSP node)
{
+ KIS_ASSERT_RECOVER_RETURN(node != activeNode());
+
if (m_d->activateNodeImpl(node)) {
emit sigUiNeedChangeActiveNode(node);
emit sigNodeActivated(node);
@@ -508,12 +513,24 @@ void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node)
}
}
-void KisNodeManager::slotUiActivatedNode(KisNodeSP node)
+void KisNodeManager::slotNonUiActivatedNode(KisNodeSP node)
{
- if (m_d->activateNodeImpl(node)) {
- emit sigNodeActivated(node);
- nodesUpdated();
+ if (node == activeNode()) return;
+ slotSomethingActivatedNodeImpl(node);
+
+ if (node) {
+ bool toggled = m_d->view->actionCollection()->action("view_show_just_the_canvas")->isChecked();
+ if (toggled) {
+ m_d->view->showFloatingMessage( activeLayer()->name(), QIcon(), 1600, KisFloatingMessage::Medium, Qt::TextSingleLine);
+ }
}
+}
+
+void KisNodeManager::slotUiActivatedNode(KisNodeSP node)
+{
+ if (node == activeNode()) return;
+
+ slotSomethingActivatedNodeImpl(node);
if (node) {
QStringList vectorTools = QStringList()
@@ -606,16 +623,22 @@ void KisNodeManager::setNodeCompositeOp(KisNodeSP node,
m_d->commandsAdapter.setCompositeOp(node, compositeOp);
}
-void KisNodeManager::setSelectedNodes(QList<KisNodeSP> nodes)
+void KisNodeManager::slotSetSelectedNodes(const KisNodeList &nodes)
{
m_d->selectedNodes = nodes;
+ emit sigUiNeedChangeSelectedNodes(nodes);
}
-QList<KisNodeSP> KisNodeManager::selectedNodes()
+KisNodeList KisNodeManager::selectedNodes()
{
return m_d->selectedNodes;
}
+KisNodeSelectionAdapter* KisNodeManager::nodeSelectionAdapter() const
+{
+ return m_d->nodeSelectionAdapter.data();
+}
+
void KisNodeManager::nodeOpacityChanged(qreal opacity, bool finalChange)
{
KisNodeSP node = activeNode();
@@ -710,7 +733,7 @@ bool scanForLastLayer(KisImageWSP image, KisNodeSP nodeToRemove)
}
/// Scan whether the node has a parent in the list of nodes
-bool scanForParent(QList<KisNodeSP> nodeList, KisNodeSP node)
+bool scanForParent(KisNodeList nodeList, KisNodeSP node)
{
KisNodeSP parent = node->parent();
@@ -742,7 +765,7 @@ void KisNodeManager::removeSingleNode(KisNodeSP node)
}
}
-void KisNodeManager::removeSelectedNodes(QList<KisNodeSP> selectedNodes)
+void KisNodeManager::removeSelectedNodes(KisNodeList selectedNodes)
{
m_d->commandsAdapter.beginMacro(kundo2_i18n("Remove Multiple Layers and Masks"));
Q_FOREACH (KisNodeSP node, selectedNodes) {
diff --git a/krita/ui/kis_node_manager.h b/krita/ui/kis_node_manager.h
index 0c80af5..99f3686 100644
--- a/krita/ui/kis_node_manager.h
+++ b/krita/ui/kis_node_manager.h
@@ -34,6 +34,7 @@ class KisFilterStrategy;
class KisViewManager;
class KisActionManager;
class KisView;
+class KisNodeSelectionAdapter;
/**
* The node manager passes requests for new layers or masks on to the mask and layer
@@ -63,6 +64,8 @@ Q_SIGNALS:
/// without telling the node manager that the node is activated,
/// preventing loops (I think...)
void sigUiNeedChangeActiveNode(KisNodeSP node);
+
+ void sigUiNeedChangeSelectedNodes(const KisNodeList &nodes);
public:
@@ -95,12 +98,9 @@ public:
*/
void setNodeCompositeOp(KisNodeSP node, const KoCompositeOp* compositeOp);
- /**
- * @brief setSelectedNodes set the list of nodes selected in the layerbox. Selected nodes are not necessarily active nodes.
- * @param nodes the selected nodes
- */
- void setSelectedNodes(QList<KisNodeSP> nodes);
- QList<KisNodeSP> selectedNodes();
+ KisNodeList selectedNodes();
+
+ KisNodeSelectionAdapter* nodeSelectionAdapter() const;
public Q_SLOTS:
@@ -201,6 +201,12 @@ public Q_SLOTS:
void slotSplitAlphaWrite();
void slotSplitAlphaSaveMerged();
+ /**
+ * @brief slotSetSelectedNodes set the list of nodes selected in the layerbox. Selected nodes are not necessarily active nodes.
+ * @param nodes the selected nodes
+ */
+ void slotSetSelectedNodes(const KisNodeList &nodes);
+
public:
@@ -217,8 +223,8 @@ private:
* to the integer range 0...255
*/
qint32 convertOpacityToInt(qreal opacity);
- void removeSelectedNodes(QList<KisNodeSP> selectedNodes);
-
+ void removeSelectedNodes(KisNodeList selectedNodes);
+ void slotSomethingActivatedNodeImpl(KisNodeSP node);
struct Private;
Private * const m_d;
diff --git a/krita/ui/kis_node_model.cpp b/krita/ui/kis_node_model.cpp
index 044bdb6..a06d076 100644
--- a/krita/ui/kis_node_model.cpp
+++ b/krita/ui/kis_node_model.cpp
@@ -45,6 +45,7 @@
#include "kis_node_dummies_graph.h"
#include "kis_model_index_converter.h"
#include "kis_model_index_converter_show_all.h"
+#include "kis_node_selection_adapter.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
@@ -55,15 +56,17 @@ struct KisNodeModel::Private
{
KisImageWSP image;
KisShapeController *shapeController = 0;
+ KisNodeSelectionAdapter *nodeSelectionAdapter;
QList<KisNodeDummy*> updateQueue;
QTimer updateTimer;
KisModelIndexConverterBase *indexConverter = 0;
KisDummiesFacadeBase *dummiesFacade = 0;
- bool needFinishRemoveRows = false;
- bool needFinishInsertRows = false;
- bool showRootLayer = false;
- bool showGlobalSelection = false;
+ bool needFinishRemoveRows;
+ bool needFinishInsertRows;
+ bool showRootLayer;
+ bool showGlobalSelection;
+ QPersistentModelIndex activeNodeIndex;
KisNodeDummy* parentOfRemovedNode = 0;
};
@@ -235,7 +238,7 @@ void KisNodeModel::connectDummies(KisNodeDummy *dummy, bool needConnect)
}
}
-void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController)
+void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController, KisNodeSelectionAdapter *nodeSelectionAdapter)
{
KisDummiesFacadeBase *oldDummiesFacade;
KisShapeController *oldShapeController;
@@ -243,6 +246,7 @@ void KisNodeModel::setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImag
oldDummiesFacade = m_d->dummiesFacade;
m_d->shapeController = shapeController;
+ m_d->nodeSelectionAdapter = nodeSelectionAdapter;
if(oldDummiesFacade && m_d->image) {
m_d->image->disconnect(this);
@@ -426,6 +430,9 @@ QVariant KisNodeModel::data(const QModelIndex &index, int role) const
KisNodeProgressProxy *proxy = node->nodeProgressProxy();
return proxy ? proxy->percentage() : -1;
}
+ case ActiveRole: {
+ return m_d->activeNodeIndex == index;
+ }
default:
if (role >= int(BeginThumbnailRole) && belongsToIsolatedGroup(node))
return node->createThumbnail(role - int(BeginThumbnailRole), role - int(BeginThumbnailRole));
@@ -448,7 +455,6 @@ bool KisNodeModel::setData(const QModelIndex &index, const QVariant &value, int
{
if (role == ActiveRole || role == AlternateActiveRole) {
-
QModelIndex parentIndex;
if (!index.isValid() && m_d->parentOfRemovedNode && m_d->dummiesFacade && m_d->indexConverter) {
parentIndex = m_d->indexConverter->indexFromDummy(m_d->parentOfRemovedNode);
@@ -467,8 +473,18 @@ bool KisNodeModel::setData(const QModelIndex &index, const QVariant &value, int
activatedNode = 0;
}
+ QModelIndex newActiveNode = activatedNode ? indexFromNode(activatedNode) : QModelIndex();
+ if (role == ActiveRole && value.toBool() &&
+ m_d->activeNodeIndex == newActiveNode) {
- emit nodeActivated(activatedNode);
+ return true;
+ }
+
+ m_d->activeNodeIndex = newActiveNode;
+
+ if (m_d->nodeSelectionAdapter) {
+ m_d->nodeSelectionAdapter->setActiveNode(activatedNode);
+ }
if (role == AlternateActiveRole) {
emit toggleIsolateActiveNode();
diff --git a/krita/ui/kis_node_model.h b/krita/ui/kis_node_model.h
index 9fd2270..ec32c3a 100644
--- a/krita/ui/kis_node_model.h
+++ b/krita/ui/kis_node_model.h
@@ -30,6 +30,7 @@ class KisDummiesFacadeBase;
class KisNodeDummy;
class KisShapeController;
class KisModelIndexConverterBase;
+class KisNodeSelectionAdapter;
/**
* KisNodeModel offers a Qt model-view compatible view of the node
@@ -57,7 +58,7 @@ public: // from QAbstractItemModel
KisNodeModel(QObject * parent);
~KisNodeModel();
- void setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController);
+ void setDummiesFacade(KisDummiesFacadeBase *dummiesFacade, KisImageWSP image, KisShapeController *shapeController, KisNodeSelectionAdapter *nodeSelectionAdapter);
KisNodeSP nodeFromIndex(const QModelIndex &index) const;
QModelIndex indexFromNode(KisNodeSP node) const;
diff --git a/krita/ui/kis_node_selection_adapter.cpp b/krita/ui/kis_node_selection_adapter.cpp
new file mode 100644
index 0000000..297790c
--- /dev/null
+++ b/krita/ui/kis_node_selection_adapter.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 Dmitry Kazakov <dimula73 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_node_selection_adapter.h"
+
+#include "kis_node_manager.h"
+#include "kis_node.h"
+
+struct KisNodeSelectionAdapter::Private
+{
+ KisNodeManager *nodeManager;
+};
+
+KisNodeSelectionAdapter::KisNodeSelectionAdapter(KisNodeManager *nodeManager)
+ : m_d(new Private)
+{
+ m_d->nodeManager = nodeManager;
+}
+
+KisNodeSelectionAdapter::~KisNodeSelectionAdapter()
+{
+}
+
+KisNodeSP KisNodeSelectionAdapter::activeNode() const
+{
+ return m_d->nodeManager->activeNode();
+}
+
+void KisNodeSelectionAdapter::setActiveNode(KisNodeSP node)
+{
+ m_d->nodeManager->slotUiActivatedNode(node);
+}
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h b/krita/ui/kis_node_selection_adapter.h
similarity index 54%
copy from krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h
copy to krita/ui/kis_node_selection_adapter.h
index b4ecb2f..2eb3e8a 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_node_juggler_compressed.h
+++ b/krita/ui/kis_node_selection_adapter.h
@@ -16,45 +16,28 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __KIS_NODE_JUGGLER_COMPRESSED_H
-#define __KIS_NODE_JUGGLER_COMPRESSED_H
+#ifndef __KIS_NODE_SELECTION_ADAPTER_H
+#define __KIS_NODE_SELECTION_ADAPTER_H
-#include <QObject>
#include <QScopedPointer>
-
-#include <kritadefaultdockers_export.h>
-#include <kundo2command.h>
#include "kis_types.h"
+#include "kritaui_export.h"
+
+class KisNodeManager;
-class KRITADEFAULTDOCKERS_EXPORT KisNodeJugglerCompressed : public QObject
+class KRITAUI_EXPORT KisNodeSelectionAdapter
{
- Q_OBJECT
public:
- KisNodeJugglerCompressed(const KUndo2MagicString &actionName, KisImageSP image, int timeout);
- ~KisNodeJugglerCompressed();
-
- void moveNode(KisNodeSP node, KisNodeSP parent, KisNodeSP above);
- void setAutoDelete(bool value);
-
- bool isEnded() const;
+ KisNodeSelectionAdapter(KisNodeManager *nodeManager);
+ ~KisNodeSelectionAdapter();
- void lowerNode(KisNodeSP node);
- void raiseNode(KisNodeSP node);
-
-public Q_SLOTS:
- void end();
-
-private Q_SLOTS:
- void slotUpdateTimeout();
- void slotEndStrokeRequested();
-
-private:
- void startTimers();
+ KisNodeSP activeNode() const;
+ void setActiveNode(KisNodeSP node);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
-#endif /* __KIS_NODE_JUGGLER_COMPRESSED_H */
+#endif /* __KIS_NODE_SELECTION_ADAPTER_H */
diff --git a/krita/ui/tests/kis_model_index_converter_test.cpp b/krita/ui/tests/kis_model_index_converter_test.cpp
index 623fb13..4aecbc6 100644
--- a/krita/ui/tests/kis_model_index_converter_test.cpp
+++ b/krita/ui/tests/kis_model_index_converter_test.cpp
@@ -36,12 +36,12 @@ void KisModelIndexConverterTest::init()
addSelectionMasks();
m_dummiesFacade->setImage(m_image);
- m_nodeModel->setDummiesFacade(m_dummiesFacade, m_image, 0);
+ m_nodeModel->setDummiesFacade(m_dummiesFacade, m_image, 0, 0);
}
void KisModelIndexConverterTest::cleanup()
{
- m_nodeModel->setDummiesFacade(0, 0, 0);
+ m_nodeModel->setDummiesFacade(0, 0, 0, 0);
m_dummiesFacade->setImage(0);
cleanupBase();
diff --git a/krita/ui/tests/kis_node_model_test.cpp b/krita/ui/tests/kis_node_model_test.cpp
index d808983..733fd80 100644
--- a/krita/ui/tests/kis_node_model_test.cpp
+++ b/krita/ui/tests/kis_node_model_test.cpp
@@ -56,14 +56,14 @@ void KisNodeModelTest::testSetImage()
{
constructImage();
m_shapeController->setImage(m_image);
- m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0);
+ m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0);
new ModelTest(m_nodeModel, this);
}
void KisNodeModelTest::testAddNode()
{
m_shapeController->setImage(m_image);
- m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0);
+ m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0);
new ModelTest(m_nodeModel, this);
constructImage();
@@ -74,7 +74,7 @@ void KisNodeModelTest::testRemoveAllNodes()
{
constructImage();
m_shapeController->setImage(m_image);
- m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0);
+ m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0);
new ModelTest(m_nodeModel, this);
m_image->removeNode(m_layer4);
@@ -87,7 +87,7 @@ void KisNodeModelTest::testRemoveIncludingRoot()
{
constructImage();
m_shapeController->setImage(m_image);
- m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0);
+ m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0);
new ModelTest(m_nodeModel, this);
m_image->removeNode(m_layer4);
@@ -103,7 +103,7 @@ void KisNodeModelTest::testSubstituteRootNode()
{
constructImage();
m_shapeController->setImage(m_image);
- m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0);
+ m_nodeModel->setDummiesFacade(m_shapeController, m_image, 0, 0);
new ModelTest(m_nodeModel, this);
m_image->flatten();
More information about the kimageshop
mailing list