Commit daa32b7a authored by Dmitry Kazakov's avatar Dmitry Kazakov

Implement basic strategies for editing the Gradient handles

Now we also have an abstract KoInteractionStrategyFactory which
can be added/removed from the Interaction tool and therefore activate/
deactivate some user interaction.
parent 9f2220ca
......@@ -179,6 +179,7 @@ set(kritaflake_SRCS
tools/KoPanToolFactory.cpp
tools/KoInteractionTool.cpp
tools/KoInteractionStrategy.cpp
tools/KoInteractionStrategyFactory.cpp
tools/KoCreateShapesTool.cpp
tools/KoCreateShapesToolFactory.cpp
tools/KoShapeRubberSelectStrategy.cpp
......
......@@ -128,6 +128,7 @@ void KoGradientBackground::paint(QPainter &painter, const KoViewConverter &/*con
QTransform gradientToUser(boundingRect.width(), 0, 0, boundingRect.height(),
boundingRect.x(), boundingRect.y());
// TODO: how about slicing the object?
QGradient g = *d->gradient;
g.setCoordinateMode(QGradient::LogicalMode);
......
/*
* Copyright (c) 2017 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 "KoInteractionStrategyFactory.h"
#include <QString>
struct KoInteractionStrategyFactory::Private
{
int priority = 0;
QString id;
};
KoInteractionStrategyFactory::KoInteractionStrategyFactory(int priority, const QString &id)
: m_d(new Private)
{
m_d->priority = priority;
m_d->id = id;
}
KoInteractionStrategyFactory::~KoInteractionStrategyFactory()
{
}
QString KoInteractionStrategyFactory::id() const
{
return m_d->id;
}
int KoInteractionStrategyFactory::priority() const
{
return m_d->priority;
}
bool KoInteractionStrategyFactory::compareLess(KoInteractionStrategyFactorySP f1, KoInteractionStrategyFactorySP f2)
{
return f1->priority() < f2->priority();
}
/*
* Copyright (c) 2017 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 KOINTERACTIONSTRATEGYFACTORY_H
#define KOINTERACTIONSTRATEGYFACTORY_H
#include <QScopedPointer>
#include <QSharedPointer>
#include "kritaflake_export.h"
class QString;
class QPainter;
class KoInteractionStrategy;
class KoPointerEvent;
class KoViewConverter;
class KoInteractionStrategyFactory;
typedef QSharedPointer<KoInteractionStrategyFactory> KoInteractionStrategyFactorySP;
class KRITAFLAKE_EXPORT KoInteractionStrategyFactory
{
public:
KoInteractionStrategyFactory(int priority, const QString &id);
virtual ~KoInteractionStrategyFactory();
QString id() const;
int priority() const;
virtual KoInteractionStrategy* createStrategy(KoPointerEvent *ev) = 0;
virtual bool hoverEvent(KoPointerEvent *ev) = 0;
virtual bool paintOnHover(QPainter &painter, const KoViewConverter &converter) = 0;
virtual bool tryUseCustomCursor() = 0;
static bool compareLess(KoInteractionStrategyFactorySP f1, KoInteractionStrategyFactorySP f2);
private:
struct Private;
QScopedPointer<Private> m_d;
};
#endif // KOINTERACTIONSTRATEGYFACTORY_H
......@@ -25,6 +25,10 @@
#include "KoCanvasBase.h"
#include "KoPanTool.h"
#include "kis_global.h"
#include "kis_assert.h"
KoInteractionTool::KoInteractionTool(KoCanvasBase *canvas)
: KoToolBase(*(new KoInteractionToolPrivate(this, canvas)))
{
......@@ -37,8 +41,15 @@ KoInteractionTool::~KoInteractionTool()
void KoInteractionTool::paint(QPainter &painter, const KoViewConverter &converter)
{
Q_D(KoInteractionTool);
if (d->currentStrategy)
if (d->currentStrategy) {
d->currentStrategy->paint(painter, converter);
} else {
Q_FOREACH (KoInteractionStrategyFactorySP factory, d->interactionFactories) {
// skip the rest of rendering if the factory asks for it
if (factory->paintOnHover(painter, converter)) break;
}
}
}
void KoInteractionTool::mousePressEvent(KoPointerEvent *event)
......@@ -48,7 +59,7 @@ void KoInteractionTool::mousePressEvent(KoPointerEvent *event)
cancelCurrentStrategy();
return;
}
d->currentStrategy = createStrategy(event);
d->currentStrategy = createStrategyBase(event);
if (d->currentStrategy == 0)
event->ignore();
}
......@@ -57,10 +68,17 @@ void KoInteractionTool::mouseMoveEvent(KoPointerEvent *event)
{
Q_D(KoInteractionTool);
d->lastPoint = event->point;
if (d->currentStrategy)
d->currentStrategy->handleMouseMove(d->lastPoint, event->modifiers());
else
else {
Q_FOREACH (KoInteractionStrategyFactorySP factory, d->interactionFactories) {
// skip the rest of rendering if the factory asks for it
if (factory->hoverEvent(event)) return;
}
event->ignore();
}
}
void KoInteractionTool::mouseReleaseEvent(KoPointerEvent *event)
......@@ -123,6 +141,75 @@ void KoInteractionTool::cancelCurrentStrategy()
}
}
KoInteractionStrategy *KoInteractionTool::createStrategyBase(KoPointerEvent *event)
{
Q_D(KoInteractionTool);
Q_FOREACH (KoInteractionStrategyFactorySP factory, d->interactionFactories) {
KoInteractionStrategy *strategy = factory->createStrategy(event);
if (strategy) {
return strategy;
}
}
return createStrategy(event);
}
void KoInteractionTool::addInteractionFactory(KoInteractionStrategyFactory *factory)
{
Q_D(KoInteractionTool);
Q_FOREACH (auto f, d->interactionFactories) {
KIS_SAFE_ASSERT_RECOVER_RETURN(f->id() != factory->id());
}
d->interactionFactories.append(toQShared(factory));
qSort(d->interactionFactories.begin(),
d->interactionFactories.end(),
KoInteractionStrategyFactory::compareLess);
}
void KoInteractionTool::removeInteractionFactory(const QString &id)
{
Q_D(KoInteractionTool);
QList<KoInteractionStrategyFactorySP>::iterator it =
d->interactionFactories.begin();
while (it != d->interactionFactories.end()) {
if ((*it)->id() == id) {
it = d->interactionFactories.erase(it);
} else {
++it;
}
}
}
bool KoInteractionTool::hasInteractioFactory(const QString &id)
{
Q_D(KoInteractionTool);
Q_FOREACH (auto f, d->interactionFactories) {
if (f->id() == id) {
return true;
}
}
return false;
}
bool KoInteractionTool::tryUseCustomCursor()
{
Q_D(KoInteractionTool);
Q_FOREACH (auto f, d->interactionFactories) {
if (f->tryUseCustomCursor()) {
return true;
}
}
return false;
}
KoInteractionTool::KoInteractionTool(KoInteractionToolPrivate &dd)
: KoToolBase(dd)
{
......
......@@ -26,6 +26,7 @@
#include "kritaflake_export.h"
class KoInteractionStrategy;
class KoInteractionStrategyFactory;
class KoInteractionToolPrivate;
#define KoInteractionTool_ID "InteractionTool"
......@@ -86,8 +87,15 @@ protected:
* Reimplement this factory method to create your strategy to be used for mouse interaction.
* @returns a new strategy, or 0 when there is nothing to do.
*/
KoInteractionStrategy *createStrategyBase(KoPointerEvent *event);
virtual KoInteractionStrategy *createStrategy(KoPointerEvent *event) = 0;
void addInteractionFactory(KoInteractionStrategyFactory *factory);
void removeInteractionFactory(const QString &id);
bool hasInteractioFactory(const QString &id);
bool tryUseCustomCursor();
private:
KoInteractionTool(const KoInteractionTool&);
KoInteractionTool& operator=(const KoInteractionTool&);
......
......@@ -23,6 +23,7 @@
#include "KoToolBase_p.h"
#include "KoInteractionStrategy.h"
#include "KoInteractionStrategyFactory.h"
class KoInteractionToolPrivate : public KoToolBasePrivate
{
......@@ -39,6 +40,7 @@ public:
QPointF lastPoint;
KoInteractionStrategy *currentStrategy;
QList<QSharedPointer<KoInteractionStrategyFactory>> interactionFactories;
};
#endif
......@@ -19,6 +19,7 @@
#include "KisHandlePainterHelper.h"
#include <QPainter>
#include "kis_algebra_2d.h"
KisHandlePainterHelper::KisHandlePainterHelper(QPainter *_painter, qreal handleRadius)
......@@ -50,6 +51,82 @@ void KisHandlePainterHelper::drawHandleRect(const QPointF &center) {
m_painter->drawPolygon(m_handlePolygon.translated(m_painterTransform.map(center)));
}
void KisHandlePainterHelper::drawGradientHandle(const QPointF &center, qreal radius) {
QPolygonF handlePolygon;
handlePolygon << QPointF(-radius, 0);
handlePolygon << QPointF(0, radius);
handlePolygon << QPointF(radius, 0);
handlePolygon << QPointF(0, -radius);
handlePolygon = m_handleTransform.map(handlePolygon);
m_painter->drawPolygon(handlePolygon.translated(m_painterTransform.map(center)));
}
void KisHandlePainterHelper::drawGradientCrossHandle(const QPointF &center, qreal radius) {
{ // Draw a cross
QPainterPath p;
p.moveTo(-radius, -radius);
p.lineTo(radius, radius);
p.moveTo(radius, -radius);
p.lineTo(-radius, radius);
p = m_handleTransform.map(p);
m_painter->drawPath(p.translated(m_painterTransform.map(center)));
}
{ // Draw a square
const qreal halfRadius = 0.5 * radius;
QPolygonF handlePolygon;
handlePolygon << QPointF(-halfRadius, 0);
handlePolygon << QPointF(0, halfRadius);
handlePolygon << QPointF(halfRadius, 0);
handlePolygon << QPointF(0, -halfRadius);
handlePolygon = m_handleTransform.map(handlePolygon);
m_painter->drawPolygon(handlePolygon.translated(m_painterTransform.map(center)));
}
}
void KisHandlePainterHelper::drawArrow(const QPointF &pos, const QPointF &from, qreal radius)
{
QPainterPath p;
QLineF line(pos, from);
line.setLength(radius);
QPointF norm = KisAlgebra2D::leftUnitNormal(pos - from);
norm *= 0.34 * radius;
p.moveTo(line.p2() + norm);
p.lineTo(line.p1());
p.lineTo(line.p2() - norm);
p.translate(-pos);
m_painter->drawPath(m_handleTransform.map(p).translated(m_painterTransform.map(pos)));
}
void KisHandlePainterHelper::drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius)
{
QPainterPath p;
p.moveTo(start);
p.lineTo(end);
m_painter->drawPath(m_painterTransform.map(p));
const qreal length = kisDistance(start, end);
const QPointF diff = end - start;
if (length > 5 * radius) {
drawArrow(start + 0.33 * diff, start, radius);
drawArrow(start + 0.66 * diff, start, radius);
} else if (length > 3 * radius) {
drawArrow(start + 0.5 * diff, start, radius);
}
}
void KisHandlePainterHelper::drawRubberLine(const QPolygonF &poly) {
m_painter->drawPolygon(m_painterTransform.map(poly));
}
......@@ -61,18 +61,40 @@ public:
*/
void drawHandleRect(const QPointF &center);
/**
* Draw a rotated handle representing the gradient handle
*/
void drawGradientHandle(const QPointF &center, qreal radius);
/**
* Draw a special handle representing the center of the gradient
*/
void drawGradientCrossHandle(const QPointF &center, qreal radius);
/**
* Draw an arrow representing gradient position
*/
void drawGradientArrow(const QPointF &start, const QPointF &end, qreal radius);
/**
* Draw a line showing the bounding box of the selection
*/
void drawRubberLine(const QPolygonF &poly);
private:
/**
* Draw a single arrow with the tip at position \p pos, directed from \p from,
* of size \p radius.
*/
void drawArrow(const QPointF &pos, const QPointF &from, qreal radius);
private:
QPainter *m_painter;
QTransform m_painterTransform;
KisAlgebra2D::DecomposedMatix m_decomposedMatrix;
QTransform m_handleTransform;
QPolygonF m_handlePolygon;
};
#endif // KISHANDLEPAINTERHELPER_H
......@@ -43,6 +43,7 @@ set(kritaui_LIB_SRCS
canvas/kis_guides_config.cpp
canvas/kis_snap_config.cpp
canvas/kis_snap_line_strategy.cpp
canvas/KisSnapPointStrategy.cpp
dialogs/kis_about_application.cpp
dialogs/kis_dlg_adj_layer_props.cc
dialogs/kis_dlg_adjustment_layer.cc
......
/*
* Copyright (c) 2017 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 "KisSnapPointStrategy.h"
#include <QPainterPath>
#include "kis_global.h"
struct KisSnapPointStrategy::Private
{
QList<QPointF> points;
};
KisSnapPointStrategy::KisSnapPointStrategy(KoSnapGuide::Strategy type)
: KoSnapStrategy(type),
m_d(new Private)
{
}
KisSnapPointStrategy::~KisSnapPointStrategy()
{
}
bool KisSnapPointStrategy::snap(const QPointF &mousePosition, KoSnapProxy *proxy, qreal maxSnapDistance)
{
Q_UNUSED(proxy);
QPointF snappedPoint = mousePosition;
qreal minDistance = std::numeric_limits<qreal>::max();
Q_FOREACH (const QPointF &pt, m_d->points) {
const qreal dist = kisDistance(mousePosition, pt);
if (dist < maxSnapDistance && dist < minDistance) {
minDistance = dist;
snappedPoint = pt;
}
}
setSnappedPosition(snappedPoint);
return minDistance < std::numeric_limits<qreal>::max();
}
QPainterPath KisSnapPointStrategy::decoration(const KoViewConverter &converter) const
{
Q_UNUSED(converter);
return QPainterPath();
}
void KisSnapPointStrategy::addPoint(const QPointF &pt)
{
m_d->points << pt;
}
/*
* Copyright (c) 2017 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 KISSNAPPOINTSTRATEGY_H
#define KISSNAPPOINTSTRATEGY_H
#include <QScopedPointer>
#include "KoSnapStrategy.h"
#include "kritaui_export.h"
/**
* The KisSnapPointStrategy class is a custom strategy that allows snapping to
* arbitrary points on canvas, not linked to any real objects. It can be used,
* for example, for snapping to the *previous position* of the handle, while it
* is dragging by the user.
*/
class KRITAUI_EXPORT KisSnapPointStrategy : public KoSnapStrategy
{
public:
KisSnapPointStrategy(KoSnapGuide::Strategy type = KoSnapGuide::CustomSnapping);
~KisSnapPointStrategy();
bool snap(const QPointF &mousePosition, KoSnapProxy * proxy, qreal maxSnapDistance) override;
QPainterPath decoration(const KoViewConverter &converter) const override;
void addPoint(const QPointF &pt);
private:
struct Private;
const QScopedPointer<Private> m_d;
};
#endif // KISSNAPPOINTSTRATEGY_H
......@@ -10,8 +10,10 @@ set ( defaulttools_SRCS
defaulttool/ShapeResizeStrategy.cpp
defaulttool/ShapeRotateStrategy.cpp
defaulttool/ShapeShearStrategy.cpp
defaulttool/ShapeGradientEditStrategy.cpp
defaulttool/SelectionDecorator.cpp
defaulttool/KoFillConfigWidget.cpp
defaulttool/KoShapeGradientHandles.cpp
connectionTool/ConnectionTool.cpp
connectionTool/ConnectionToolFactory.cpp
......
......@@ -52,6 +52,7 @@
#include <KoSnapGuide.h>
#include <KoStrokeConfigWidget.h>
#include "kis_action_registry.h"
#include <KoInteractionStrategyFactory.h>
#include <KoIcon.h>
......@@ -116,10 +117,6 @@ public:
void finishInteraction(Qt::KeyboardModifiers /*modifiers*/) override {}
void paint(QPainter &painter, const KoViewConverter &converter) {
SelectionDecorator decorator(tool()->canvas()->resourceManager());
decorator.setSelection(tool()->canvas()->shapeManager()->selection());
decorator.setHandleRadius(handleRadius());
decorator.paint(painter, converter);
}
};
......@@ -132,15 +129,94 @@ public:
}
void paint(QPainter &painter, const KoViewConverter &converter) {
SelectionDecorator decorator(tool()->canvas()->resourceManager());
decorator.setSelection(tool()->canvas()->shapeManager()->selection());
decorator.setHandleRadius(handleRadius());
decorator.paint(painter, converter);
KoShapeRubberSelectStrategy::paint(painter, converter);
}
};
#include <KoGradientBackground.h>
#include "KoShapeGradientHandles.h"
#include "ShapeGradientEditStrategy.h"
class DefaultTool::MoveGradientHandleInteractionFactory : public KoInteractionStrategyFactory
{
public:
MoveGradientHandleInteractionFactory(DefaultTool *_q)
: KoInteractionStrategyFactory(0, "move_gradient_handle"),
q(_q)
{
}
KoInteractionStrategy* createStrategy(KoPointerEvent *ev) override
{
m_currentHandle = handleAt(ev->point);
if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) {
KoShape *shape = onlyEditableShape();
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, 0);
return new ShapeGradientEditStrategy(q, shape, m_currentHandle.type, ev->point);
}
return 0;
}
bool hoverEvent(KoPointerEvent *ev) override
{
m_currentHandle = handleAt(ev->point);
return false;
}
bool paintOnHover(QPainter &painter, const KoViewConverter &converter) override
{
return false;
}
bool tryUseCustomCursor() {
if (m_currentHandle.type != KoShapeGradientHandles::Handle::None) {
q->useCursor(Qt::OpenHandCursor);
}
return m_currentHandle.type != KoShapeGradientHandles::Handle::None;
}
private:
KoShape* onlyEditableShape() const {
KoSelection *selection = q->koSelection();
QList<KoShape*> shapes = selection->selectedEditableShapes();
KoShape *shape = 0;
if (shapes.size() == 1) {
shape = shapes.first();
}
return shape;
}
KoShapeGradientHandles::Handle handleAt(const QPointF &pos) {
KoShapeGradientHandles::Handle result;
KoShape *shape = onlyEditableShape();
if (shape) {
qreal minDistanceSq = std::numeric_limits<qreal>::max();