Commit 58d33ff7 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Ported the Transform Tool onto primary/secondary actions framework

Transform tool was the last tool not ported and filtering events directly.
Now it also uses the predefined actions. The resulting architecture doesn't
seem to be ideal, and looks a bit complicated with lots of methods calling
each other and forwarding 9 methods for every strategy. Probably, we should
use some boost::function-based action assignment here. Then we will be able
to map/remaps actions quite easily.

This patch implements shift-gesture action and ctrl-base inversion for the
transformation tool. Right not it is almost ready for usage. The only thing
left is "build-up vs wash mode" switch (if needed) and better UI.

CCMAIL:kimageshop@kde.org
parent 4814ee73
......@@ -76,6 +76,15 @@ Point normalize(const Point &a)
return (1.0 / length) * a;
}
/**
* Usual sign() function with positive zero
*/
template <typename T>
T signPZ(T x) {
const T zeroValue(0);
return x >= T(0) ? T(1) : T(-1);
}
template <class T>
T leftUnitNormal(const T &a)
{
......
......@@ -7,6 +7,7 @@ set(kritatooltransform_PART_SRCS
kis_transform_strategy_base.cpp
kis_warp_transform_strategy.cpp
kis_cage_transform_strategy.cpp
kis_simplified_action_policy_strategy.cpp
kis_liquify_transform_strategy.cpp
kis_liquify_paint_helper.cpp
kis_liquify_paintop.cpp
......
......@@ -135,7 +135,7 @@ struct KisFreeTransformStrategy::Private
KisFreeTransformStrategy::KisFreeTransformStrategy(const KisCoordinatesConverter *converter,
ToolTransformArgs &currentArgs,
TransformTransactionProperties &transaction)
: KisTransformStrategyBase(converter),
: KisSimplifiedActionPolicyStrategy(converter),
m_d(new Private(this, converter, currentArgs, transaction))
{
}
......
......@@ -22,7 +22,7 @@
#include <QObject>
#include <QScopedPointer>
#include "kis_transform_strategy_base.h"
#include "kis_simplified_action_policy_strategy.h"
class QPointF;
class QPainter;
......@@ -33,7 +33,7 @@ class TransformTransactionProperties;
class QCursor;
class QImage;
class KisFreeTransformStrategy : public KisTransformStrategyBase
class KisFreeTransformStrategy : public KisSimplifiedActionPolicyStrategy
{
Q_OBJECT
public:
......
......@@ -57,6 +57,14 @@ public:
m_size = value;
}
static qreal minSize() {
return 5.0;
}
static qreal maxSize() {
return 1000.0;
}
qreal amount() const {
return m_amount;
}
......
......@@ -23,6 +23,8 @@
#include <QPointF>
#include <QPainter>
#include "KoPointerEvent.h"
#include "kis_coordinates_converter.h"
#include "tool_transform_args.h"
#include "transform_transaction_properties.h"
......@@ -30,6 +32,7 @@
#include "kis_cursor.h"
#include "kis_transform_utils.h"
#include "kis_algebra_2d.h"
#include "kis_transform_utils.h"
#include "kis_liquify_paint_helper.h"
#include "kis_liquify_transform_worker.h"
......@@ -67,7 +70,12 @@ struct KisLiquifyTransformStrategy::Private
/// custom members ///
QImage transformedImage;
QPointF lastMousePos;
// size-gesture-related
QPointF lastMouseWidgetPos;
QPointF startResizeImagePos;
QPoint startResizeGlobalCursorPos;
KisLiquifyPaintHelper helper;
void recalculateTransformations();
......@@ -78,8 +86,7 @@ KisLiquifyTransformStrategy::KisLiquifyTransformStrategy(const KisCoordinatesCon
ToolTransformArgs &currentArgs,
TransformTransactionProperties &transaction)
: KisTransformStrategyBase(converter),
m_d(new Private(this, converter, currentArgs, transaction))
: m_d(new Private(this, converter, currentArgs, transaction))
{
}
......@@ -136,10 +143,8 @@ bool KisLiquifyTransformStrategy::beginPrimaryAction(KoPointerEvent *event)
return true;
}
void KisLiquifyTransformStrategy::continuePrimaryAction(KoPointerEvent *event, bool specialModifierActve)
void KisLiquifyTransformStrategy::continuePrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(specialModifierActve);
m_d->helper.continuePaint(event);
m_d->recalculateTransformations();
......@@ -156,11 +161,89 @@ bool KisLiquifyTransformStrategy::endPrimaryAction(KoPointerEvent *event)
return true;
}
void KisLiquifyTransformStrategy::hoverPrimaryAction(KoPointerEvent *event)
void KisLiquifyTransformStrategy::hoverActionCommon(KoPointerEvent *event)
{
m_d->helper.hoverPaint(event);
}
void KisLiquifyTransformStrategy::activateAlternateAction(KisTool::AlternateAction action)
{
if (action == KisTool::PickFgNode || action == KisTool::PickBgNode ||
action == KisTool::PickFgImage || action == KisTool::PickBgImage) {
KisLiquifyProperties *props = m_d->currentArgs.liquifyProperties();
props->setReverseDirection(!props->reverseDirection());
emit requestUpdateOptionWidget();
}
}
void KisLiquifyTransformStrategy::deactivateAlternateAction(KisTool::AlternateAction action)
{
if (action == KisTool::PickFgNode || action == KisTool::PickBgNode ||
action == KisTool::PickFgImage || action == KisTool::PickBgImage) {
KisLiquifyProperties *props = m_d->currentArgs.liquifyProperties();
props->setReverseDirection(!props->reverseDirection());
emit requestUpdateOptionWidget();
}
}
bool KisLiquifyTransformStrategy::beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action)
{
if (action == KisTool::ChangeSize) {
QPointF widgetPoint = m_d->converter->documentToWidget(event->point);
m_d->lastMouseWidgetPos = widgetPoint;
m_d->startResizeImagePos = m_d->converter->documentToImage(event->point);
m_d->startResizeGlobalCursorPos = QCursor::pos();
return true;
} else if (action == KisTool::PickFgNode || action == KisTool::PickBgNode ||
action == KisTool::PickFgImage || action == KisTool::PickBgImage) {
return beginPrimaryAction(event);
}
return false;
}
void KisLiquifyTransformStrategy::continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action)
{
if (action == KisTool::ChangeSize) {
QPointF widgetPoint = m_d->converter->documentToWidget(event->point);
QPointF diff = widgetPoint - m_d->lastMouseWidgetPos;
KisLiquifyProperties *props = m_d->currentArgs.liquifyProperties();
const qreal linearizedOffset = diff.x() / KisTransformUtils::scaleFromAffineMatrix(m_d->converter->imageToWidgetTransform());
const qreal newSize = qBound(props->minSize(), props->size() + linearizedOffset, props->maxSize());
props->setSize(newSize);
m_d->currentArgs.saveLiquifyTransformMode();
m_d->lastMouseWidgetPos = widgetPoint;
emit requestCursorOutlineUpdate(m_d->startResizeImagePos);
} else if (action == KisTool::PickFgNode || action == KisTool::PickBgNode ||
action == KisTool::PickFgImage || action == KisTool::PickBgImage) {
return continuePrimaryAction(event);
}
}
bool KisLiquifyTransformStrategy::endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action)
{
Q_UNUSED(event);
if (action == KisTool::ChangeSize) {
QCursor::setPos(m_d->startResizeGlobalCursorPos);
return true;
} else if (action == KisTool::PickFgNode || action == KisTool::PickBgNode ||
action == KisTool::PickFgImage || action == KisTool::PickBgImage) {
return endPrimaryAction(event);
}
return false;
}
inline QPointF KisLiquifyTransformStrategy::Private::imageToThumb(const QPointF &pt, bool useFlakeOptimization)
{
return useFlakeOptimization ? converter->imageToDocument(converter->documentToFlake((pt))) : q->thumbToImageTransform().inverted().map(pt);
......
......@@ -48,22 +48,26 @@ public:
QCursor getCurrentCursor() const;
QPainterPath getCursorOutline() const;
void externalConfigChanged();
bool acceptsClicks() const;
using KisTransformStrategyBase::beginPrimaryAction;
using KisTransformStrategyBase::continuePrimaryAction;
using KisTransformStrategyBase::endPrimaryAction;
using KisTransformStrategyBase::hoverPrimaryAction;
void externalConfigChanged();
bool beginPrimaryAction(KoPointerEvent *event);
void continuePrimaryAction(KoPointerEvent *event, bool specialModifierActve);
void continuePrimaryAction(KoPointerEvent *event);
bool endPrimaryAction(KoPointerEvent *event);
void hoverPrimaryAction(KoPointerEvent *event);
void hoverActionCommon(KoPointerEvent *event);
bool acceptsClicks() const;
void activateAlternateAction(KisTool::AlternateAction action);
void deactivateAlternateAction(KisTool::AlternateAction action);
bool beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action);
void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action);
bool endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action);
signals:
void requestCanvasUpdate();
void requestUpdateOptionWidget();
void requestCursorOutlineUpdate(const QPointF &imagePoint);
private:
class Private;
......
......@@ -114,7 +114,7 @@ struct KisPerspectiveTransformStrategy::Private
KisPerspectiveTransformStrategy::KisPerspectiveTransformStrategy(const KisCoordinatesConverter *converter,
ToolTransformArgs &currentArgs,
TransformTransactionProperties &transaction)
: KisTransformStrategyBase(converter),
: KisSimplifiedActionPolicyStrategy(converter),
m_d(new Private(this, converter, currentArgs, transaction))
{
}
......
......@@ -22,7 +22,7 @@
#include <QObject>
#include <QScopedPointer>
#include "kis_transform_strategy_base.h"
#include "kis_simplified_action_policy_strategy.h"
class QPointF;
class QPainter;
......@@ -33,7 +33,7 @@ class TransformTransactionProperties;
class QCursor;
class QImage;
class KisPerspectiveTransformStrategy : public KisTransformStrategyBase
class KisPerspectiveTransformStrategy : public KisSimplifiedActionPolicyStrategy
{
Q_OBJECT
public:
......
/*
* Copyright (c) 2014 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 "kis_simplified_action_policy_strategy.h"
#include "KoPointerEvent.h"
#include "kis_coordinates_converter.h"
struct KisSimplifiedActionPolicyStrategy::Private
{
Private(const KisCoordinatesConverter *_converter)
: converter(_converter) {}
const KisCoordinatesConverter *converter;
bool changeSizeModifierActive;
bool anyPickerModifierActive;
};
KisSimplifiedActionPolicyStrategy::KisSimplifiedActionPolicyStrategy(const KisCoordinatesConverter *_converter)
: m_d(new Private(_converter))
{
}
KisSimplifiedActionPolicyStrategy::~KisSimplifiedActionPolicyStrategy()
{
}
bool KisSimplifiedActionPolicyStrategy::beginPrimaryAction(KoPointerEvent *event)
{
return beginPrimaryAction(m_d->converter->documentToImage(event->point));
}
void KisSimplifiedActionPolicyStrategy::continuePrimaryAction(KoPointerEvent *event)
{
continuePrimaryAction(m_d->converter->documentToImage(event->point), false);
}
void KisSimplifiedActionPolicyStrategy::hoverActionCommon(KoPointerEvent *event)
{
hoverActionCommon(m_d->converter->documentToImage(event->point));
}
bool KisSimplifiedActionPolicyStrategy::endPrimaryAction(KoPointerEvent *event)
{
Q_UNUSED(event);
return endPrimaryAction();
}
void KisSimplifiedActionPolicyStrategy::activateAlternateAction(KisTool::AlternateAction action)
{
if (action == KisTool::ChangeSize) {
m_d->changeSizeModifierActive = true;
} else if (action == KisTool::PickFgNode || action == KisTool::PickBgNode ||
action == KisTool::PickFgImage || action == KisTool::PickBgImage) {
m_d->anyPickerModifierActive = true;
}
}
void KisSimplifiedActionPolicyStrategy::deactivateAlternateAction(KisTool::AlternateAction action)
{
if (action == KisTool::ChangeSize) {
m_d->changeSizeModifierActive = false;
} else if (action == KisTool::PickFgNode || action == KisTool::PickBgNode ||
action == KisTool::PickFgImage || action == KisTool::PickBgImage) {
m_d->anyPickerModifierActive = false;
}
}
bool KisSimplifiedActionPolicyStrategy::beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action)
{
Q_UNUSED(action);
if (!m_d->changeSizeModifierActive && !m_d->anyPickerModifierActive) return false;
return beginPrimaryAction(m_d->converter->documentToImage(event->point));
}
void KisSimplifiedActionPolicyStrategy::continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action)
{
Q_UNUSED(action);
if (!m_d->changeSizeModifierActive && !m_d->anyPickerModifierActive) return;
continuePrimaryAction(m_d->converter->documentToImage(event->point), m_d->changeSizeModifierActive);
}
bool KisSimplifiedActionPolicyStrategy::endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action)
{
Q_UNUSED(action);
if (!m_d->changeSizeModifierActive && !m_d->anyPickerModifierActive) return false;
return endPrimaryAction();
}
void KisSimplifiedActionPolicyStrategy::hoverActionCommon(const QPointF &pt)
{
setTransformFunction(pt, m_d->anyPickerModifierActive);
}
/*
* Copyright (c) 2014 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 __KIS_SIMPLIFIED_ACTION_POLICY_STRATEGY_H
#define __KIS_SIMPLIFIED_ACTION_POLICY_STRATEGY_H
#include <QScopedPointer>
#include "kis_transform_strategy_base.h"
class KoPointerEvent;
class KisCoordinatesConverter;
class KisSimplifiedActionPolicyStrategy : public KisTransformStrategyBase
{
public:
KisSimplifiedActionPolicyStrategy(const KisCoordinatesConverter *_converter);
~KisSimplifiedActionPolicyStrategy();
virtual bool beginPrimaryAction(KoPointerEvent *event);
virtual void continuePrimaryAction(KoPointerEvent *event);
virtual bool endPrimaryAction(KoPointerEvent *event);
virtual void hoverActionCommon(KoPointerEvent *event);
virtual void activateAlternateAction(KisTool::AlternateAction action);
virtual void deactivateAlternateAction(KisTool::AlternateAction action);
virtual bool beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action);
virtual void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action);
virtual bool endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action);
protected:
virtual void setTransformFunction(const QPointF &mousePos, bool perspectiveModifierActive) = 0;
virtual bool beginPrimaryAction(const QPointF &pt) = 0;
virtual void continuePrimaryAction(const QPointF &pt, bool specialModifierActve) = 0;
virtual bool endPrimaryAction() = 0;
virtual void hoverActionCommon(const QPointF &pt);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif /* __KIS_SIMPLIFIED_ACTION_POLICY_STRATEGY_H */
......@@ -123,6 +123,8 @@ KisToolTransform::KisToolTransform(KoCanvasBase * canvas)
connect(m_warpStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_cageStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_liquifyStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_liquifyStrategy.data(), SIGNAL(requestCursorOutlineUpdate(const QPointF&)), SLOT(cursorOutlineUpdateRequested(const QPointF&)));
connect(m_liquifyStrategy.data(), SIGNAL(requestUpdateOptionWidget()), SLOT(updateOptionWidget()));
connect(m_freeStrategy.data(), SIGNAL(requestCanvasUpdate()), SLOT(canvasUpdateRequested()));
connect(m_freeStrategy.data(), SIGNAL(requestResetRotationCenterButtons()), SLOT(resetRotationCenterButtonsRequested()));
connect(m_freeStrategy.data(), SIGNAL(requestShowImageTooBig(bool)), SLOT(imageTooBigRequested(bool)));
......@@ -215,7 +217,7 @@ void KisToolTransform::setFunctionalCursor()
}
}
void KisToolTransform::updateCursorOutline(const QPointF &imagePos)
void KisToolTransform::cursorOutlineUpdateRequested(const QPointF &imagePos)
{
QRect canvasUpdateRect;
......@@ -240,36 +242,142 @@ void KisToolTransform::updateCursorOutline(const QPointF &imagePos)
}
}
void KisToolTransform::mousePressEvent(KoPointerEvent *event)
void KisToolTransform::beginActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action)
{
if (!PRESS_CONDITION_OM(event, KisTool::HOVER_MODE, Qt::LeftButton, Qt::ControlModifier | Qt::ShiftModifier)) {
KisTool::mousePressEvent(event);
if (!nodeEditable()) {
event->ignore();
return;
}
KisImageWSP kisimage = image();
if (!currentNode())
return;
if (!m_strokeData.strokeId()) {
startStroke(m_currentArgs.mode());
} else {
bool result = false;
setMode(KisTool::PAINT_MODE);
if (kisimage && event->button() == Qt::LeftButton) {
if (!m_strokeData.strokeId()) {
startStroke(m_currentArgs.mode());
setMode(KisTool::HOVER_MODE);
if (usePrimaryAction) {
result = currentStrategy()->beginPrimaryAction(event);
} else {
if (!currentStrategy()->beginPrimaryAction(event)) {
setMode(KisTool::HOVER_MODE);
}
result = currentStrategy()->beginAlternateAction(event, action);
}
m_actuallyMoveWhileSelected = false;
if (result) {
setMode(KisTool::PAINT_MODE);
}
}
m_actuallyMoveWhileSelected = false;
outlineChanged();
}
void KisToolTransform::continueActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action)
{
if (mode() != KisTool::PAINT_MODE) return;
m_actuallyMoveWhileSelected = true;
if (usePrimaryAction) {
currentStrategy()->continuePrimaryAction(event);
} else {
currentStrategy()->continueAlternateAction(event, action);
}
updateOptionWidget();
outlineChanged();
}
void KisToolTransform::endActionImpl(KoPointerEvent *event, bool usePrimaryAction, KisTool::AlternateAction action)
{
if (mode() != KisTool::PAINT_MODE) return;
setMode(KisTool::HOVER_MODE);
if (m_actuallyMoveWhileSelected ||
currentStrategy()->acceptsClicks()) {
bool result = false;
if (usePrimaryAction) {
result = currentStrategy()->endPrimaryAction(event);
} else {
result = currentStrategy()->endAlternateAction(event, action);
}
if (result) {
commitChanges();
}
outlineChanged();
}
updateOptionWidget();
updateApplyResetAvailability();
}
void KisToolTransform::beginPrimaryAction(KoPointerEvent *event)
{
beginActionImpl(event, true, KisTool::NONE);
}
void KisToolTransform::continuePrimaryAction(KoPointerEvent *event)
{
continueActionImpl(event, true, KisTool::NONE);
}
void KisToolTransform::endPrimaryAction(KoPointerEvent *event)
{
endActionImpl(event, true, KisTool::NONE);
}
void KisToolTransform::activateAlternateAction(AlternateAction action)
{
currentStrategy()->activateAlternateAction(action);
}
void KisToolTransform::deactivateAlternateAction(AlternateAction action)
{
currentStrategy()->deactivateAlternateAction(action);
}
void KisToolTransform::beginAlternateAction(KoPointerEvent *event, AlternateAction action)
{
beginActionImpl(event, false, action);
}
void KisToolTransform::continueAlternateAction(KoPointerEvent *event, AlternateAction action)
{
continueActionImpl(event, false, action);
}
void KisToolTransform::endAlternateAction(KoPointerEvent *event, AlternateAction action)