[krita/krita/4.1] /: FEATURE: Undo for Move Tool

Boudewijn Rempt null at kde.org
Thu Sep 13 12:59:27 BST 2018


Git commit 0857f999ae86b1a403cee9141877da5c10c58db9 by Boudewijn Rempt, on behalf of Dmitry Kazakov.
Committed on 13/09/2018 at 11:59.
Pushed by rempt into branch 'krita/4.1'.

FEATURE: Undo for Move Tool

Now move tool also supports undo for intermediate actions

BUG:392014
CC:kimageshop at kde.org

M  +2    -0    libs/ui/CMakeLists.txt
A  +46   -0    libs/ui/tool/KisToolChangesTracker.cpp     [License: UNKNOWN]  *
A  +31   -0    libs/ui/tool/KisToolChangesTracker.h     [License: UNKNOWN]  *
A  +18   -0    libs/ui/tool/KisToolChangesTrackerData.cpp     [License: UNKNOWN]  *
A  +19   -0    libs/ui/tool/KisToolChangesTrackerData.h     [License: UNKNOWN]  *
M  +140  -116  plugins/tools/basictools/kis_tool_move.cc
M  +11   -7    plugins/tools/basictools/kis_tool_move.h
M  +3    -0    plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp
M  +2    -0    plugins/tools/basictools/kis_tool_movetooloptionswidget.h
M  +0    -1    plugins/tools/tool_transform2/CMakeLists.txt
M  +9    -5    plugins/tools/tool_transform2/kis_tool_transform.cc
M  +3    -3    plugins/tools/tool_transform2/kis_tool_transform.h
M  +5    -0    plugins/tools/tool_transform2/tool_transform_args.cc
M  +4    -2    plugins/tools/tool_transform2/tool_transform_args.h
D  +0    -49   plugins/tools/tool_transform2/tool_transform_changes_tracker.cpp
D  +0    -54   plugins/tools/tool_transform2/tool_transform_changes_tracker.h

The files marked with a * at the end have a non valid license. Please read: https://community.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page.


https://commits.kde.org/krita/0857f999ae86b1a403cee9141877da5c10c58db9

diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt
index 1e8feaa7a43..a231cb8ced1 100644
--- a/libs/ui/CMakeLists.txt
+++ b/libs/ui/CMakeLists.txt
@@ -168,6 +168,8 @@ set(kritaui_LIB_SRCS
     opengl/kis_texture_tile_info_pool.cpp
     opengl/KisOpenGLUpdateInfoBuilder.cpp
     kis_fps_decoration.cpp
+    tool/KisToolChangesTracker.cpp
+    tool/KisToolChangesTrackerData.cpp
     tool/kis_selection_tool_helper.cpp
     tool/kis_selection_tool_config_widget_helper.cpp
     tool/kis_rectangle_constraint_widget.cpp
diff --git a/libs/ui/tool/KisToolChangesTracker.cpp b/libs/ui/tool/KisToolChangesTracker.cpp
new file mode 100644
index 00000000000..b740644148c
--- /dev/null
+++ b/libs/ui/tool/KisToolChangesTracker.cpp
@@ -0,0 +1,46 @@
+#include "KisToolChangesTracker.h"
+
+#include "kis_global.h"
+#include <QSharedPointer>
+
+struct KisToolChangesTracker::Private {
+    QList<KisToolChangesTrackerDataSP> undoStack;
+};
+
+
+KisToolChangesTracker::KisToolChangesTracker()
+    : m_d(new Private)
+{
+}
+
+KisToolChangesTracker::~KisToolChangesTracker()
+{
+}
+
+void KisToolChangesTracker::commitConfig(KisToolChangesTrackerDataSP state)
+{
+    m_d->undoStack.append(state);
+}
+
+void KisToolChangesTracker::requestUndo()
+{
+    if (m_d->undoStack.size() > 1) {
+        m_d->undoStack.removeLast();
+        emit sigConfigChanged(m_d->undoStack.last());
+    }
+}
+
+KisToolChangesTrackerDataSP KisToolChangesTracker::lastState() const
+{
+    return !m_d->undoStack.isEmpty() ? m_d->undoStack.last() : 0;
+}
+
+void KisToolChangesTracker::reset()
+{
+    m_d->undoStack.clear();
+}
+
+bool KisToolChangesTracker::isEmpty() const
+{
+    return m_d->undoStack.isEmpty();
+}
diff --git a/libs/ui/tool/KisToolChangesTracker.h b/libs/ui/tool/KisToolChangesTracker.h
new file mode 100644
index 00000000000..e81c5685126
--- /dev/null
+++ b/libs/ui/tool/KisToolChangesTracker.h
@@ -0,0 +1,31 @@
+#ifndef KISTOOLCHANGESTRACKER_H
+#define KISTOOLCHANGESTRACKER_H
+
+#include "kritaui_export.h"
+#include <QScopedPointer>
+#include "KisToolChangesTrackerData.h"
+
+class KRITAUI_EXPORT KisToolChangesTracker :public QObject
+{
+    Q_OBJECT
+
+public:
+    KisToolChangesTracker();
+    ~KisToolChangesTracker();
+
+    void commitConfig(KisToolChangesTrackerDataSP state);
+    void requestUndo();
+    KisToolChangesTrackerDataSP lastState() const;
+    void reset();
+
+    bool isEmpty() const;
+
+Q_SIGNALS:
+    void sigConfigChanged(KisToolChangesTrackerDataSP state);
+
+private:
+    struct Private;
+    const QScopedPointer<Private> m_d;
+};
+
+#endif // KISTOOLCHANGESTRACKER_H
diff --git a/libs/ui/tool/KisToolChangesTrackerData.cpp b/libs/ui/tool/KisToolChangesTrackerData.cpp
new file mode 100644
index 00000000000..3cce59c497c
--- /dev/null
+++ b/libs/ui/tool/KisToolChangesTrackerData.cpp
@@ -0,0 +1,18 @@
+#include "KisToolChangesTrackerData.h"
+
+struct KisToolChangesTrackerDataSPRegistrar {
+    KisToolChangesTrackerDataSPRegistrar() {
+        qRegisterMetaType<KisToolChangesTrackerDataSP>("KisToolChangesTrackerDataSP");
+    }
+};
+static KisToolChangesTrackerDataSPRegistrar __registrar;
+
+
+KisToolChangesTrackerData::~KisToolChangesTrackerData()
+{
+}
+
+KisToolChangesTrackerData *KisToolChangesTrackerData::clone() const
+{
+    return new KisToolChangesTrackerData(*this);
+}
diff --git a/libs/ui/tool/KisToolChangesTrackerData.h b/libs/ui/tool/KisToolChangesTrackerData.h
new file mode 100644
index 00000000000..6f879a91b53
--- /dev/null
+++ b/libs/ui/tool/KisToolChangesTrackerData.h
@@ -0,0 +1,19 @@
+#ifndef KISTOOLCHANGESTRACKERDATA_H
+#define KISTOOLCHANGESTRACKERDATA_H
+
+#include <QObject>
+#include "kritaui_export.h"
+#include <QSharedPointer>
+
+class KRITAUI_EXPORT KisToolChangesTrackerData
+{
+public:
+    virtual ~KisToolChangesTrackerData();
+    virtual KisToolChangesTrackerData* clone() const;
+};
+
+typedef QSharedPointer<KisToolChangesTrackerData> KisToolChangesTrackerDataSP;
+
+Q_DECLARE_METATYPE(KisToolChangesTrackerDataSP)
+
+#endif // KISTOOLCHANGESTRACKERDATA_H
diff --git a/plugins/tools/basictools/kis_tool_move.cc b/plugins/tools/basictools/kis_tool_move.cc
index 1472fc8ef23..c1d3a815547 100644
--- a/plugins/tools/basictools/kis_tool_move.cc
+++ b/plugins/tools/basictools/kis_tool_move.cc
@@ -44,6 +44,20 @@
 
 #include "kis_node_manager.h"
 #include "kis_signals_blocker.h"
+#include <boost/operators.hpp>
+
+struct KisToolMoveState : KisToolChangesTrackerData, boost::equality_comparable<KisToolMoveState>
+{
+    KisToolMoveState(QPoint _accumulatedOffset) : accumulatedOffset(_accumulatedOffset) {}
+    KisToolChangesTrackerData* clone() const { return new KisToolMoveState(*this); }
+
+    bool operator ==(const KisToolMoveState &rhs) {
+        return accumulatedOffset == rhs.accumulatedOffset;
+    }
+
+    QPoint accumulatedOffset;
+};
+
 
 KisToolMove::KisToolMove(KoCanvasBase * canvas)
         :  KisTool(canvas, KisCursor::moveCursor())
@@ -52,7 +66,6 @@ KisToolMove::KisToolMove(KoCanvasBase * canvas)
 
     setObjectName("tool_move");
     m_optionsWidget = 0;
-    m_moveInProgress = false;
     QAction *a;
 
     KisActionRegistry *actionRegistry = KisActionRegistry::instance();
@@ -90,6 +103,10 @@ KisToolMove::KisToolMove(KoCanvasBase * canvas)
 
     m_showCoordinatesAction = actionRegistry->makeQAction("movetool-show-coordinates", this);
     addAction("movetool-show-coordinates", m_showCoordinatesAction);
+
+    connect(&m_changesTracker,
+            SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)),
+            SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP)));
 }
 
 KisToolMove::~KisToolMove()
@@ -141,20 +158,16 @@ bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
         return false;
     }
 
-    initHandles(nodes);
-
     /**
      * If the target node has changed, the stroke should be
      * restarted. Otherwise just continue processing current node.
      */
-    if (m_strokeId) {
-        if (KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) {
-            return true;
-        } else {
-            endStroke();
-        }
+    if (m_strokeId && !tryEndPreviousStroke(nodes)) {
+        return true;
     }
 
+    initHandles(nodes);
+
     KisStrokeStrategy *strategy;
 
     KisPaintLayerSP paintLayer = node ?
@@ -177,50 +190,92 @@ bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
     m_currentlyProcessingNodes = nodes;
     m_accumulatedOffset = QPoint();
 
+    KIS_SAFE_ASSERT_RECOVER(m_changesTracker.isEmpty()) {
+        m_changesTracker.reset();
+    }
+    commitChanges();
+
     return true;
 }
 
-void KisToolMove::moveDiscrete(MoveDirection direction, bool big)
+QPoint KisToolMove::currentOffset() const
 {
-    if (mode() == KisTool::PAINT_MODE) return;  // Don't interact with dragging
-    if (!currentNode()->isEditable()) return; // Don't move invisible nodes
+    return m_accumulatedOffset + m_dragPos - m_dragStart;
+}
 
-    if (startStrokeImpl(MoveSelectedLayer, 0)) {
-        setMode(KisTool::PAINT_MODE);
-    }
+void KisToolMove::notifyGuiAfterMove(bool showFloatingMessage)
+{
+    if (!m_optionsWidget) return;
 
-    // Larger movement if "shift" key is pressed.
-    qreal scale = big ? m_optionsWidget->moveScale() : 1.0;
-    qreal moveStep = m_optionsWidget->moveStep() * scale;
+    const QPoint currentTopLeft = m_handlesRect.topLeft() + currentOffset();
 
-    QPoint offset = direction == Up   ? QPoint( 0, -moveStep) :
-                    direction == Down ? QPoint( 0,  moveStep) :
-                    direction == Left ? QPoint(-moveStep,  0) :
-                                        QPoint( moveStep,  0) ;
+    KisSignalsBlocker b(m_optionsWidget);
+    emit moveInNewPosition(currentTopLeft);
 
+    // TODO: fetch this info not from options widget, but from config
     const bool showCoordinates = m_optionsWidget->showCoordinates();
 
-    if (showCoordinates) {
+    if (showCoordinates && showFloatingMessage) {
         KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
         kisCanvas->viewManager()->
             showFloatingMessage(
                 i18nc("floating message in move tool",
                       "X: %1 px, Y: %2 px",
-                      (m_startPosition + offset).x(),
-                      (m_startPosition + offset).y()),
+                      currentTopLeft.x(),
+                      currentTopLeft.y()),
                 QIcon(), 1000, KisFloatingMessage::High);
     }
+}
 
-    KisSignalsBlocker b(m_optionsWidget);
-    emit moveInNewPosition(m_startPosition + offset);
+bool KisToolMove::tryEndPreviousStroke(KisNodeList nodes)
+{
+    if (!m_strokeId) return false;
+
+    bool strokeEnded = false;
+
+    if (!KritaUtils::compareListsUnordered(nodes, m_currentlyProcessingNodes)) {
+        endStroke();
+        strokeEnded = true;
+    }
+
+    return strokeEnded;
+}
+
+void KisToolMove::commitChanges()
+{
+    KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId);
+
+    QSharedPointer<KisToolMoveState> newState(new KisToolMoveState(m_accumulatedOffset));
+    KisToolMoveState *lastState = dynamic_cast<KisToolMoveState*>(m_changesTracker.lastState().data());
+    if (lastState && *lastState == *newState) return;
+
+    m_changesTracker.commitConfig(newState);
+}
+
+void KisToolMove::moveDiscrete(MoveDirection direction, bool big)
+{
+    if (mode() == KisTool::PAINT_MODE) return;  // Don't interact with dragging
+    if (!currentNode()->isEditable()) return; // Don't move invisible nodes
+
+    if (startStrokeImpl(MoveSelectedLayer, 0)) {
+        setMode(KisTool::PAINT_MODE);
+    }
+
+    // Larger movement if "shift" key is pressed.
+    qreal scale = big ? m_optionsWidget->moveScale() : 1.0;
+    qreal moveStep = m_optionsWidget->moveStep() * scale;
 
-    m_startPosition += offset;
+    const QPoint offset =
+        direction == Up   ? QPoint( 0, -moveStep) :
+        direction == Down ? QPoint( 0,  moveStep) :
+        direction == Left ? QPoint(-moveStep,  0) :
+        QPoint( moveStep,  0) ;
 
-    image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset + offset));
     m_accumulatedOffset += offset;
+    image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
 
-    m_moveInProgress = false;
-    emit moveInProgressChanged();
+    notifyGuiAfterMove();
+    commitChanges();
     setMode(KisTool::HOVER_MODE);
 }
 
@@ -240,7 +295,7 @@ void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter)
 
     if (m_dragInProgress) {
         QPainterPath handles;
-        handles.addRect(m_handlesRect.translated(m_pos));
+        handles.addRect(m_handlesRect.translated(currentOffset()));
 
         QPainterPath path = pixelToView(handles);
         paintToolOutline(&gc, path);
@@ -249,6 +304,12 @@ void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter)
 
 void KisToolMove::initHandles(const KisNodeList &nodes)
 {
+    /**
+     * The handles should be initialized only once, **before** the start of
+     * the stroke. If the nodes change, we should restart the stroke.
+     */
+    KIS_SAFE_ASSERT_RECOVER_NOOP(!m_strokeId);
+
     m_handlesRect = QRect();
     for (KisNodeSP node : nodes) {
         node->exactBounds();
@@ -257,9 +318,6 @@ void KisToolMove::initHandles(const KisNodeList &nodes)
     if (image()->globalSelection()) {
         m_handlesRect &= image()->globalSelection()->selectedRect();
     }
-    if (m_handlesRect.topLeft() != m_startPosition) {
-        m_handlesRect.moveTopLeft(m_startPosition);
-    }
 }
 
 void KisToolMove::deactivate()
@@ -280,9 +338,8 @@ void KisToolMove::requestStrokeCancellation()
 
 void KisToolMove::requestUndoDuringStroke()
 {
-    // we shouldn't cancel the stroke on Ctrl+Z, because it will not only
-    // cancel the stroke, but also undo the previous command, which we haven't
-    // yet pushed to the stack
+    if (!m_strokeId) return;
+    m_changesTracker.requestUndo();
 }
 
 void KisToolMove::beginPrimaryAction(KoPointerEvent *event)
@@ -334,15 +391,16 @@ void KisToolMove::startAction(KoPointerEvent *event, MoveToolMode mode)
 {
     QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
     m_dragStart = pos;
-    m_pos = QPoint();
-    m_moveInProgress = true;
+    m_dragPos = pos;
     m_dragInProgress = true;
-    emit moveInProgressChanged();
 
     if (startStrokeImpl(mode, &pos)) {
         setMode(KisTool::PAINT_MODE);
     } else {
         event->ignore();
+        m_dragPos = QPoint();
+        m_dragStart = QPoint();
+        m_dragInProgress = false;
     }
     m_canvas->updateCanvas();
 }
@@ -354,27 +412,12 @@ void KisToolMove::continueAction(KoPointerEvent *event)
     if (!m_strokeId) return;
 
     QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
-
-    const bool showCoordinates =
-        m_optionsWidget ? m_optionsWidget->showCoordinates() : true;
-
-    if (showCoordinates) {
-        KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
-        kisCanvas->viewManager()->
-                showFloatingMessage(
-                    i18nc("floating message in move tool",
-                          "X: %1 px, Y: %2 px",
-                          (m_startPosition + (pos - m_dragStart)).x(),
-                          (m_startPosition + (pos - m_dragStart)).y()),
-                    QIcon(), 1000, KisFloatingMessage::High);
-    }
-
-    KisSignalsBlocker b(m_optionsWidget);
-    emit moveInNewPosition(m_startPosition + (pos - m_dragStart));
-
     pos = applyModifiers(event->modifiers(), pos);
+    m_dragPos = pos;
+
     drag(pos);
-    m_pos = pos - m_dragStart;
+    notifyGuiAfterMove();
+
     m_canvas->updateCanvas();
 }
 
@@ -387,11 +430,15 @@ void KisToolMove::endAction(KoPointerEvent *event)
     QPoint pos = convertToPixelCoordAndSnap(event).toPoint();
     pos = applyModifiers(event->modifiers(), pos);
     drag(pos);
-    m_pos = pos - m_dragStart;
 
+    m_accumulatedOffset += m_dragPos - m_dragStart;
+    m_dragStart = QPoint();
+    m_dragPos = QPoint();
     m_dragInProgress = false;
-    m_startPosition += pos - m_dragStart;
-    m_accumulatedOffset += pos - m_dragStart;
+    commitChanges();
+
+    notifyGuiAfterMove();
+
     m_canvas->updateCanvas();
 }
 
@@ -412,9 +459,22 @@ void KisToolMove::endStroke()
     KisImageSP image = currentImage();
     image->endStroke(m_strokeId);
     m_strokeId.clear();
+    m_changesTracker.reset();
     m_currentlyProcessingNodes.clear();
-    m_moveInProgress = false;
-    emit moveInProgressChanged();
+    m_accumulatedOffset = QPoint();
+}
+
+void KisToolMove::slotTrackerChangedConfig(KisToolChangesTrackerDataSP state)
+{
+    KIS_SAFE_ASSERT_RECOVER_RETURN(m_strokeId);
+
+    KisToolMoveState *newState = dynamic_cast<KisToolMoveState*>(state.data());
+    KIS_SAFE_ASSERT_RECOVER_RETURN(newState);
+
+    if (mode() == KisTool::PAINT_MODE) return;  // Don't interact with dragging
+    m_accumulatedOffset = newState->accumulatedOffset;
+    image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
+    notifyGuiAfterMove();
 }
 
 void KisToolMove::cancelStroke()
@@ -424,15 +484,10 @@ void KisToolMove::cancelStroke()
     KisImageSP image = currentImage();
     image->cancelStroke(m_strokeId);
     m_strokeId.clear();
+    m_changesTracker.reset();
     m_currentlyProcessingNodes.clear();
-    m_moveInProgress = false;
-    emit moveInProgressChanged();
-
-    // we should reset m_startPosition into the original value when
-    // the stroke is cancelled
-    KisCanvas2 *canvas = dynamic_cast<KisCanvas2*>(this->canvas());
-    canvas->viewManager()->blockUntilOperationsFinishedForced(image);
-    slotNodeChanged(this->selectedNodes());
+    m_accumulatedOffset = QPoint();
+    notifyGuiAfterMove();
 }
 
 QWidget* KisToolMove::createOptionWidget()
@@ -454,10 +509,11 @@ QWidget* KisToolMove::createOptionWidget()
 
     m_showCoordinatesAction->setChecked(m_optionsWidget->showCoordinates());
 
-    m_optionsWidget->slotSetTranslate(m_startPosition);
+    m_optionsWidget->slotSetTranslate(m_handlesRect.topLeft() + currentOffset());
 
     connect(m_optionsWidget, SIGNAL(sigSetTranslateX(int)), SLOT(moveBySpinX(int)));
     connect(m_optionsWidget, SIGNAL(sigSetTranslateY(int)), SLOT(moveBySpinY(int)));
+    connect(m_optionsWidget, SIGNAL(sigRequestCommitOffsetChanges()), this, SLOT(commitChanges()));
 
     connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)));
 
@@ -476,11 +532,6 @@ KisToolMove::MoveToolMode KisToolMove::moveToolMode() const
     return MoveSelectedLayer;
 }
 
-bool KisToolMove::moveInProgress() const
-{
-    return m_moveInProgress;
-}
-
 QPoint KisToolMove::applyModifiers(Qt::KeyboardModifiers modifiers, QPoint pos)
 {
     QPoint move = pos - m_dragStart;
@@ -508,18 +559,11 @@ void KisToolMove::moveBySpinX(int newX)
         setMode(KisTool::PAINT_MODE);
     }
 
-    int offsetX = newX - m_startPosition.x();
-    QPoint offset = QPoint(offsetX, 0);
-
-    KisSignalsBlocker b(m_optionsWidget);
-    emit moveInNewPosition(m_startPosition + offset);
+    m_accumulatedOffset.rx() =  newX - m_handlesRect.x();
 
-    image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset + offset));
-    m_accumulatedOffset += offset;
-    m_startPosition += offset;
+    image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
 
-    m_moveInProgress = false;
-    emit moveInProgressChanged();
+    notifyGuiAfterMove(false);
     setMode(KisTool::HOVER_MODE);
 }
 
@@ -532,40 +576,20 @@ void KisToolMove::moveBySpinY(int newY)
         setMode(KisTool::PAINT_MODE);
     }
 
-    int offsetY = newY - m_startPosition.y();
-    QPoint offset = QPoint(0, offsetY);
-
-    KisSignalsBlocker b(m_optionsWidget);
-    emit moveInNewPosition(m_startPosition + offset);
+    m_accumulatedOffset.ry() =  newY - m_handlesRect.y();
 
-    image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset + offset));
-    m_accumulatedOffset += offset;
-    m_startPosition += offset;
+    image()->addJob(m_strokeId, new MoveStrokeStrategy::Data(m_accumulatedOffset));
 
-    m_moveInProgress = false;
-    emit moveInProgressChanged();
+    notifyGuiAfterMove(false);
     setMode(KisTool::HOVER_MODE);
 }
 
 void KisToolMove::slotNodeChanged(KisNodeList nodes)
 {
-    QRect totalBounds;
-
-    Q_FOREACH (KisNodeSP node, nodes) {
-        if (node && node->projection()) {
-            totalBounds |= node->projection()->nonDefaultPixelArea();
-        }
-    }
-
-    if (image()->globalSelection()) {
-        totalBounds &= image()->globalSelection()->selectedRect();
+    if (m_strokeId && !tryEndPreviousStroke(nodes)) {
+        return;
     }
 
-    m_startPosition = totalBounds.topLeft();
-
-    if (m_optionsWidget)
-    {
-        KisSignalsBlocker b(m_optionsWidget);
-        m_optionsWidget->slotSetTranslate(m_startPosition);
-    }
+    initHandles(nodes);
+    notifyGuiAfterMove(false);
 }
diff --git a/plugins/tools/basictools/kis_tool_move.h b/plugins/tools/basictools/kis_tool_move.h
index d2dfa55727c..8ed06499663 100644
--- a/plugins/tools/basictools/kis_tool_move.h
+++ b/plugins/tools/basictools/kis_tool_move.h
@@ -30,6 +30,7 @@
 #include <QWidget>
 #include <QGroupBox>
 #include <QRadioButton>
+#include "KisToolChangesTracker.h"
 
 #include "kis_canvas2.h"
 
@@ -42,7 +43,6 @@ class KisToolMove : public KisTool
 {
     Q_OBJECT
     Q_ENUMS(MoveToolMode);
-    Q_PROPERTY(bool moveInProgress READ moveInProgress NOTIFY moveInProgressChanged);
 public:
     KisToolMove(KoCanvasBase * canvas);
     ~KisToolMove() override;
@@ -104,7 +104,6 @@ public:
     void updateUIUnit(int newUnit);
 
     MoveToolMode moveToolMode() const;
-    bool moveInProgress() const;
 
     void setShowCoordinates(bool value);
 
@@ -115,10 +114,10 @@ public Q_SLOTS:
     void moveBySpinY(int newY);
 
     void slotNodeChanged(KisNodeList nodes);
+    void commitChanges();
 
 Q_SIGNALS:
     void moveToolModeChanged();
-    void moveInProgressChanged();
     void moveInNewPosition(QPoint);
 
 private:
@@ -128,8 +127,14 @@ private:
 
     bool startStrokeImpl(MoveToolMode mode, const QPoint *pos);
 
+    QPoint currentOffset() const;
+    void notifyGuiAfterMove(bool showFloatingMessage = true);
+    bool tryEndPreviousStroke(KisNodeList nodes);
+
+
 private Q_SLOTS:
     void endStroke();
+    void slotTrackerChangedConfig(KisToolChangesTrackerDataSP state);
 
 private:
 
@@ -137,11 +142,8 @@ private:
     QPoint m_dragStart; ///< Point where current cursor dragging began
     QPoint m_accumulatedOffset; ///< Total offset including multiple clicks, up/down/left/right keys, etc. added together
 
-    QPoint m_startPosition;
-
     KisStrokeId m_strokeId;
 
-    bool m_moveInProgress;
     KisNodeList m_currentlyProcessingNodes;
 
     int m_resolution;
@@ -150,9 +152,11 @@ private:
 
     KisCanvas2* m_canvas;
 
-    QPoint m_pos;
+    QPoint m_dragPos;
     QRect m_handlesRect;
     bool m_dragInProgress = false;
+
+    KisToolChangesTracker m_changesTracker;
 };
 
 
diff --git a/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp b/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp
index c0c0dddfadf..2aa47948efa 100644
--- a/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp
+++ b/plugins/tools/basictools/kis_tool_movetooloptionswidget.cpp
@@ -85,6 +85,9 @@ void MoveToolOptionsWidget::updateUIUnit(int newUnit)
     spinMoveStep->blockSignals(true);
     spinMoveStep->setValue(valueForUI);
     spinMoveStep->blockSignals(false);
+
+    connect(translateXBox, SIGNAL(editingFinished()), SIGNAL(sigRequestCommitOffsetChanges()));
+    connect(translateYBox, SIGNAL(editingFinished()), SIGNAL(sigRequestCommitOffsetChanges()));
 }
 
 void MoveToolOptionsWidget::on_spinMoveStep_valueChanged(double UIMoveStep)
diff --git a/plugins/tools/basictools/kis_tool_movetooloptionswidget.h b/plugins/tools/basictools/kis_tool_movetooloptionswidget.h
index f1434bfd208..7749f0e0541 100644
--- a/plugins/tools/basictools/kis_tool_movetooloptionswidget.h
+++ b/plugins/tools/basictools/kis_tool_movetooloptionswidget.h
@@ -64,6 +64,8 @@ Q_SIGNALS:
     void sigSetTranslateX(int value);
     void sigSetTranslateY(int value);
 
+    void sigRequestCommitOffsetChanges();
+
 private:
     void updateUIUnit(int newUnit);
     void setMoveToolMode(KisToolMove::MoveToolMode newMode);
diff --git a/plugins/tools/tool_transform2/CMakeLists.txt b/plugins/tools/tool_transform2/CMakeLists.txt
index ce61896b4fa..e90230c48e6 100644
--- a/plugins/tools/tool_transform2/CMakeLists.txt
+++ b/plugins/tools/tool_transform2/CMakeLists.txt
@@ -7,7 +7,6 @@ set(kritatooltransform_SOURCES
     tool_transform_args.cc
     kis_transform_mask_adapter.cpp
     kis_animated_transform_parameters.cpp
-    tool_transform_changes_tracker.cpp
     kis_tool_transform.cc
     kis_tool_transform_config_widget.cpp
     kis_transform_strategy_base.cpp
diff --git a/plugins/tools/tool_transform2/kis_tool_transform.cc b/plugins/tools/tool_transform2/kis_tool_transform.cc
index 5bbaec35b60..09878a6da93 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform.cc
+++ b/plugins/tools/tool_transform2/kis_tool_transform.cc
@@ -91,7 +91,6 @@
 KisToolTransform::KisToolTransform(KoCanvasBase * canvas)
     : KisTool(canvas, KisCursor::rotateCursor())
     , m_workRecursively(true)
-    , m_changesTracker(&m_transaction)
     , m_warpStrategy(
         new KisWarpTransformStrategy(
             dynamic_cast<KisCanvas2*>(canvas)->coordinatesConverter(),
@@ -150,8 +149,8 @@ KisToolTransform::KisToolTransform(KoCanvasBase * canvas)
     connect(m_perspectiveStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
     connect(m_perspectiveStrategy.data(), SIGNAL(requestShowImageTooBig(bool)), SLOT(imageTooBigRequested(bool)));
 
-    connect(&m_changesTracker, SIGNAL(sigConfigChanged()),
-            this, SLOT(slotTrackerChangedConfig()));
+    connect(&m_changesTracker, SIGNAL(sigConfigChanged(KisToolChangesTrackerDataSP)),
+            this, SLOT(slotTrackerChangedConfig(KisToolChangesTrackerDataSP)));
 }
 
 KisToolTransform::~KisToolTransform()
@@ -996,11 +995,16 @@ void KisToolTransform::commitChanges()
 {
     if (!m_strokeData.strokeId()) return;
 
-    m_changesTracker.commitConfig(m_currentArgs);
+    m_changesTracker.commitConfig(toQShared(m_currentArgs.clone()));
 }
 
-void KisToolTransform::slotTrackerChangedConfig()
+void KisToolTransform::slotTrackerChangedConfig(KisToolChangesTrackerDataSP status)
 {
+    const ToolTransformArgs *newArgs = dynamic_cast<const ToolTransformArgs*>(status.data());
+    KIS_SAFE_ASSERT_RECOVER_RETURN(newArgs);
+
+    *m_transaction.currentConfig() = *newArgs;
+
     slotUiChangedConfig();
     updateOptionWidget();
 }
diff --git a/plugins/tools/tool_transform2/kis_tool_transform.h b/plugins/tools/tool_transform2/kis_tool_transform.h
index 15e034c5330..e03b2f0dec6 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform.h
+++ b/plugins/tools/tool_transform2/kis_tool_transform.h
@@ -46,7 +46,7 @@
 
 
 #include "tool_transform_args.h"
-#include "tool_transform_changes_tracker.h"
+#include "KisToolChangesTracker.h"
 #include "kis_tool_transform_config_widget.h"
 #include "transform_transaction_properties.h"
 
@@ -295,7 +295,7 @@ private:
     QPointer<KisCanvas2> m_canvas;
 
     TransformTransactionProperties m_transaction;
-    TransformChangesTracker m_changesTracker;
+    KisToolChangesTracker m_changesTracker;
 
 
     /// actions for the context click menu
@@ -333,7 +333,7 @@ private:
     QPainterPath m_cursorOutline;
 
 private Q_SLOTS:
-    void slotTrackerChangedConfig();
+    void slotTrackerChangedConfig(KisToolChangesTrackerDataSP status);
     void slotUiChangedConfig();
     void slotApplyTransform();
     void slotResetTransform();
diff --git a/plugins/tools/tool_transform2/tool_transform_args.cc b/plugins/tools/tool_transform2/tool_transform_args.cc
index 1a2612d47e7..7453961e8e3 100644
--- a/plugins/tools/tool_transform2/tool_transform_args.cc
+++ b/plugins/tools/tool_transform2/tool_transform_args.cc
@@ -121,6 +121,11 @@ ToolTransformArgs::ToolTransformArgs(const ToolTransformArgs& args)
     init(args);
 }
 
+KisToolChangesTrackerData *ToolTransformArgs::clone() const
+{
+    return new ToolTransformArgs(*this);
+}
+
 ToolTransformArgs& ToolTransformArgs::operator=(const ToolTransformArgs& args)
 {
     clear();
diff --git a/plugins/tools/tool_transform2/tool_transform_args.h b/plugins/tools/tool_transform2/tool_transform_args.h
index b04e2ec3639..073e15f8eb9 100644
--- a/plugins/tools/tool_transform2/tool_transform_args.h
+++ b/plugins/tools/tool_transform2/tool_transform_args.h
@@ -28,7 +28,7 @@
 #include "kis_liquify_properties.h"
 #include "kritatooltransform_export.h"
 #include "kis_global.h"
-
+#include "KisToolChangesTrackerData.h"
 
 #include <QScopedPointer>
 class KisLiquifyTransformWorker;
@@ -41,7 +41,7 @@ class QDomElement;
  * memory.
  */
 
-class KRITATOOLTRANSFORM_EXPORT ToolTransformArgs
+class KRITATOOLTRANSFORM_EXPORT ToolTransformArgs : public KisToolChangesTrackerData
 {
 public:
     enum TransformMode {FREE_TRANSFORM = 0,
@@ -62,6 +62,8 @@ public:
      */
     ToolTransformArgs(const ToolTransformArgs& args);
 
+    KisToolChangesTrackerData *clone() const;
+
     /**
      * If mode is warp, original and transformed vector points will be of size 0.
      * Use setPoints method to set those vectors.
diff --git a/plugins/tools/tool_transform2/tool_transform_changes_tracker.cpp b/plugins/tools/tool_transform2/tool_transform_changes_tracker.cpp
deleted file mode 100644
index 68a81950027..00000000000
--- a/plugins/tools/tool_transform2/tool_transform_changes_tracker.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *  Copyright (c) 2013 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 "tool_transform_changes_tracker.h"
-
-
-TransformChangesTracker::TransformChangesTracker(TransformTransactionProperties *transaction)
-    : m_transaction(transaction)
-{
-}
-
-void TransformChangesTracker::commitConfig(const ToolTransformArgs &config)
-{
-    m_config.append(config);
-}
-
-void TransformChangesTracker::requestUndo()
-{
-    if (m_config.size() > 1) {
-        m_config.removeLast();
-        *m_transaction->currentConfig() = m_config.last();
-        emit sigConfigChanged();
-    }
-}
-
-void TransformChangesTracker::reset()
-{
-    m_config.clear();
-}
-
-bool TransformChangesTracker::isEmpty() const
-{
-    return m_config.isEmpty();
-}
diff --git a/plugins/tools/tool_transform2/tool_transform_changes_tracker.h b/plugins/tools/tool_transform2/tool_transform_changes_tracker.h
deleted file mode 100644
index c5bf20a1473..00000000000
--- a/plugins/tools/tool_transform2/tool_transform_changes_tracker.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- *  Copyright (c) 2013 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.
- */
-
-#ifndef __TOOL_TRANSFORM_CHANGES_TRACKER_H
-#define __TOOL_TRANSFORM_CHANGES_TRACKER_H
-
-#include <QObject>
-
-#include "tool_transform_args.h"
-#include "transform_transaction_properties.h"
-
-
-/**
- * This class is supposed to support undo operations for the Transform
- * Tool. Note, that the transform tool is not going to support redo()
- * operations, so we do not use QUndoStack here to not complicate the
- * structure of classes.
- */
-class TransformChangesTracker : public QObject
-{
-    Q_OBJECT
-public:
-    TransformChangesTracker(TransformTransactionProperties *transaction);
-
-    void commitConfig(const ToolTransformArgs &config);
-    void requestUndo();
-    void reset();
-
-    bool isEmpty() const;
-
-Q_SIGNALS:
-    void sigConfigChanged();
-
-private:
-    QList<ToolTransformArgs> m_config;
-    TransformTransactionProperties *m_transaction;
-};
-
-#endif /* __TOOL_TRANSFORM_CHANGES_TRACKER_H */


More information about the kimageshop mailing list