Commit 21889437 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Make Move Tool asynchronous

Calling blockUntilOperationsFinished() from tool event handler makes
KisShortcutMatcher to be entered recursively. It basically breaks events
flow and makes KisShortcutMatcher crazy.

I tried to move blocking into the shortcuts matcher itself (so that
it could treat recursive entrance correctly), but it doesn't handle
special cases like KisToolTransform::activate(), which should also
block (if we keep them blocking).

CCBUG:409275
parent 26e236d5
......@@ -57,10 +57,13 @@ MoveStrokeStrategy::MoveStrokeStrategy(KisNodeList nodes,
}
setSupportsWrapAroundMode(true);
enableJob(KisSimpleStrokeStrategy::JOB_INIT, true, KisStrokeJobData::BARRIER);
}
MoveStrokeStrategy::MoveStrokeStrategy(const MoveStrokeStrategy &rhs)
: KisStrokeStrategyUndoCommandBased(rhs),
: QObject(),
KisStrokeStrategyUndoCommandBased(rhs),
m_nodes(rhs.m_nodes),
m_blacklistedNodes(rhs.m_blacklistedNodes),
m_updatesFacade(rhs.m_updatesFacade),
......@@ -86,11 +89,16 @@ void MoveStrokeStrategy::saveInitialNodeOffsets(KisNodeSP node)
void MoveStrokeStrategy::initStrokeCallback()
{
QRect handlesRect;
Q_FOREACH(KisNodeSP node, m_nodes) {
saveInitialNodeOffsets(node);
handlesRect |= node->exactBounds();
}
KisStrokeStrategyUndoCommandBased::initStrokeCallback();
emit sigHandlesRectCalculated(handlesRect);
}
void MoveStrokeStrategy::finishStrokeCallback()
......
......@@ -20,6 +20,7 @@
#define __MOVE_STROKE_STRATEGY_H
#include <QHash>
#include <QObject>
#include "kritaui_export.h"
#include "kis_stroke_strategy_undo_command_based.h"
......@@ -31,8 +32,9 @@ class KisUpdatesFacade;
class KisPostExecutionUndoAdapter;
class KRITAUI_EXPORT MoveStrokeStrategy : public KisStrokeStrategyUndoCommandBased
class KRITAUI_EXPORT MoveStrokeStrategy : public QObject, public KisStrokeStrategyUndoCommandBased
{
Q_OBJECT
public:
class Data : public KisStrokeJobData {
public:
......@@ -68,6 +70,9 @@ public:
KisStrokeStrategy* createLodClone(int levelOfDetail) override;
Q_SIGNALS:
void sigHandlesRectCalculated(const QRect &handlesRect);
private:
MoveStrokeStrategy(const MoveStrokeStrategy &rhs);
void setUndoEnabled(bool value);
......
......@@ -17,6 +17,7 @@ set(kritadefaulttools_SOURCES
kis_tool_move.cc
kis_tool_movetooloptionswidget.cpp
strokes/move_selection_stroke_strategy.cpp
KisMoveBoundsCalculationJob.cpp
kis_tool_multihand.cpp
kis_tool_multihand_config.cpp
kis_tool_pencil.cc
......
/*
* Copyright (c) 2019 Dmitry Kazakov <dimula73@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 "KisMoveBoundsCalculationJob.h"
#include "kis_node.h"
#include "kis_selection.h"
KisMoveBoundsCalculationJob::KisMoveBoundsCalculationJob(KisNodeList nodes,
KisSelectionSP selection,
QObject *requestedBy)
: m_nodes(nodes),
m_selection(selection),
m_requestedBy(requestedBy)
{
setExclusive(true);
}
void KisMoveBoundsCalculationJob::run()
{
QRect handlesRect;
Q_FOREACH (KisNodeSP node, m_nodes) {
handlesRect |= node->exactBounds();
}
if (m_selection) {
handlesRect &= m_selection->selectedExactRect();
}
emit sigCalcualtionFinished(handlesRect);
}
bool KisMoveBoundsCalculationJob::overrides(const KisSpontaneousJob *_otherJob)
{
const KisMoveBoundsCalculationJob *otherJob =
dynamic_cast<const KisMoveBoundsCalculationJob*>(_otherJob);
return otherJob && otherJob->m_requestedBy == m_requestedBy;
}
int KisMoveBoundsCalculationJob::levelOfDetail() const
{
return 0;
}
/*
* Copyright (c) 2019 Dmitry Kazakov <dimula73@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 KISMOVEBOUNDSCALCULATIONJOB_H
#define KISMOVEBOUNDSCALCULATIONJOB_H
#include <QObject>
#include "kis_spontaneous_job.h"
#include "kis_types.h"
#include "kis_selection.h"
class KisMoveBoundsCalculationJob : public QObject, public KisSpontaneousJob
{
Q_OBJECT
public:
KisMoveBoundsCalculationJob(KisNodeList nodes, KisSelectionSP selection, QObject *requestedBy);
void run() override;
bool overrides(const KisSpontaneousJob *otherJob) override;
int levelOfDetail() const override;
Q_SIGNALS:
void sigCalcualtionFinished(const QRect &bounds);
private:
KisNodeList m_nodes;
KisSelectionSP m_selection;
QObject *m_requestedBy;
};
#endif // KISMOVEBOUNDSCALCULATIONJOB_H
......@@ -43,8 +43,11 @@
#include <KisDocument.h>
#include "kis_node_manager.h"
#include "kis_selection_manager.h"
#include "kis_signals_blocker.h"
#include <boost/operators.hpp>
#include "KisMoveBoundsCalculationJob.h"
struct KisToolMoveState : KisToolChangesTrackerData, boost::equality_comparable<KisToolMoveState>
{
......@@ -90,6 +93,7 @@ KisToolMove::KisToolMove(KoCanvasBase *canvas)
connect(this, SIGNAL(moveInNewPosition(QPoint)), m_optionsWidget, SLOT(slotSetTranslate(QPoint)), Qt::UniqueConnection);
connect(qobject_cast<KisCanvas2*>(canvas)->viewManager()->nodeManager(), SIGNAL(sigUiNeedChangeSelectedNodes(KisNodeList)), this, SLOT(slotNodeChanged(KisNodeList)), Qt::UniqueConnection);
connect(qobject_cast<KisCanvas2*>(canvas)->viewManager()->selectionManager(), SIGNAL(currentSelectionChanged()), this, SLOT(slotSelectionChanged()), Qt::UniqueConnection);
}
KisToolMove::~KisToolMove()
......@@ -165,21 +169,6 @@ bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
return true;
}
/**
* FIXME: The move tool is not completely asynchronous, it
* needs the content of the layer and/or selection to calculate
* the bounding rectange. Technically, we can move its calculation
* into the stroke itself and pass it back to the tool via a signal.
*
* But currently, we will just disable starting a new stroke
* asynchronously.
*/
if (!blockUntilOperationsFinished()) {
return false;
}
initHandles(nodes);
KisStrokeStrategy *strategy;
KisPaintLayerSP paintLayer = node ?
......@@ -189,16 +178,32 @@ bool KisToolMove::startStrokeImpl(MoveToolMode mode, const QPoint *pos)
(!selection->selectedRect().isEmpty() &&
!selection->selectedExactRect().isEmpty())) {
strategy =
MoveSelectionStrokeStrategy *moveStrategy =
new MoveSelectionStrokeStrategy(paintLayer,
selection,
image.data(),
image.data());
connect(moveStrategy,
SIGNAL(sigHandlesRectCalculated(const QRect&)),
SLOT(slotHandlesRectCalculated(const QRect&)));
strategy = moveStrategy;
} else {
strategy =
MoveStrokeStrategy *moveStrategy =
new MoveStrokeStrategy(nodes, image.data(), image.data());
connect(moveStrategy,
SIGNAL(sigHandlesRectCalculated(const QRect&)),
SLOT(slotHandlesRectCalculated(const QRect&)));
strategy = moveStrategy;
}
// disable outline feedback until the stroke calcualtes
// correct bounding rect
m_handlesRect = QRect();
m_strokeId = image->startStroke(strategy);
m_currentlyProcessingNodes = nodes;
m_accumulatedOffset = QPoint();
......@@ -219,6 +224,7 @@ QPoint KisToolMove::currentOffset() const
void KisToolMove::notifyGuiAfterMove(bool showFloatingMessage)
{
if (!m_optionsWidget) return;
if (m_handlesRect.isEmpty()) return;
const QPoint currentTopLeft = m_handlesRect.topLeft() + currentOffset();
......@@ -265,6 +271,12 @@ void KisToolMove::commitChanges()
m_changesTracker.commitConfig(newState);
}
void KisToolMove::slotHandlesRectCalculated(const QRect &handlesRect)
{
m_handlesRect = handlesRect;
notifyGuiAfterMove(false);
}
void KisToolMove::moveDiscrete(MoveDirection direction, bool big)
{
if (mode() == KisTool::PAINT_MODE) return; // Don't interact with dragging
......@@ -333,7 +345,7 @@ void KisToolMove::paint(QPainter& gc, const KoViewConverter &converter)
{
Q_UNUSED(converter);
if (m_strokeId) {
if (m_strokeId && !m_handlesRect.isEmpty()) {
QPainterPath handles;
handles.addRect(m_handlesRect.translated(currentOffset()));
......@@ -342,24 +354,6 @@ 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();
m_handlesRect |= node->exactBounds();
}
if (image()->globalSelection()) {
m_handlesRect &= image()->globalSelection()->selectedExactRect();
}
}
void KisToolMove::deactivate()
{
m_actionConnections.clear();
......@@ -653,14 +647,36 @@ void KisToolMove::moveBySpinY(int newY)
setMode(KisTool::HOVER_MODE);
}
void KisToolMove::requestHandlesRectUpdate()
{
KisResourcesSnapshotSP resources =
new KisResourcesSnapshot(image(), currentNode(), canvas()->resourceManager());
KisSelectionSP selection = resources->activeSelection();
KisMoveBoundsCalculationJob *job = new KisMoveBoundsCalculationJob(this->selectedNodes(),
selection, this);
connect(job,
SIGNAL(sigCalcualtionFinished(const QRect&)),
SLOT(slotHandlesRectCalculated(const QRect &)));
KisImageSP image = this->image();
image->addSpontaneousJob(job);
notifyGuiAfterMove(false);
}
void KisToolMove::slotNodeChanged(KisNodeList nodes)
{
if (m_strokeId && !tryEndPreviousStroke(nodes)) {
return;
}
requestHandlesRectUpdate();
}
initHandles(nodes);
notifyGuiAfterMove(false);
void KisToolMove::slotSelectionChanged()
{
if (m_strokeId) return;
requestHandlesRectUpdate();
}
QList<QAction *> KisToolMoveFactory::createActionsImpl()
......
......@@ -102,7 +102,6 @@ public:
void endAction(KoPointerEvent *event);
void paint(QPainter& gc, const KoViewConverter &converter) override;
void initHandles(const KisNodeList &nodes);
QWidget *createOptionWidget() override;
void updateUIUnit(int newUnit);
......@@ -118,8 +117,11 @@ public Q_SLOTS:
void moveBySpinY(int newY);
void slotNodeChanged(KisNodeList nodes);
void slotSelectionChanged();
void commitChanges();
void slotHandlesRectCalculated(const QRect &handlesRect);
Q_SIGNALS:
void moveToolModeChanged();
void moveInNewPosition(QPoint);
......@@ -135,6 +137,7 @@ private:
void notifyGuiAfterMove(bool showFloatingMessage = true);
bool tryEndPreviousStroke(KisNodeList nodes);
KisNodeList fetchSelectedNodes(MoveToolMode mode, const QPoint *pixelPoint, KisSelectionSP selection);
void requestHandlesRectUpdate();
private Q_SLOTS:
......
......@@ -47,7 +47,8 @@ MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(KisPaintLayerSP paintLa
}
MoveSelectionStrokeStrategy::MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs)
: KisStrokeStrategyUndoCommandBased(rhs),
: QObject(),
KisStrokeStrategyUndoCommandBased(rhs),
m_paintLayer(rhs.m_paintLayer),
m_selection(rhs.m_selection),
m_updatesFacade(rhs.m_updatesFacade)
......@@ -84,6 +85,8 @@ void MoveSelectionStrokeStrategy::initStrokeCallback()
m_initialDeviceOffset = QPoint(movedDevice->x(), movedDevice->y());
m_selection->setVisible(false);
emit sigHandlesRectCalculated(movedDevice->exactBounds());
}
void MoveSelectionStrokeStrategy::finishStrokeCallback()
......
......@@ -21,13 +21,17 @@
#include "kis_stroke_strategy_undo_command_based.h"
#include "kis_types.h"
#include "kis_selection.h"
#include "kis_paint_layer.h"
class KisPostExecutionUndoAdapter;
class KisUpdatesFacade;
class MoveSelectionStrokeStrategy : public KisStrokeStrategyUndoCommandBased
class MoveSelectionStrokeStrategy : public QObject, public KisStrokeStrategyUndoCommandBased
{
Q_OBJECT
public:
MoveSelectionStrokeStrategy(KisPaintLayerSP paintLayer,
KisSelectionSP selection,
......@@ -39,6 +43,9 @@ public:
void cancelStrokeCallback() override;
void doStrokeCallback(KisStrokeJobData *data) override;
Q_SIGNALS:
void sigHandlesRectCalculated(const QRect &handlesRect);
private:
MoveSelectionStrokeStrategy(const MoveSelectionStrokeStrategy &rhs);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment