Commit 80497587 authored by Agata Cacko's avatar Agata Cacko

Allow Clone Brush to reset origin after every stroke

This commit introduces the behaviour of Clone Brush
that allows for always starting a new stroke
as if the offset wasn't set yet, which means it always
starts from the same source/origin point.
Usecases: painting multiple leaves, flowers, crosses etc.
in less regular way than what could be created with regular
Clone Brush.


Test Plan:
- save and load brush with and without the new option enabled
- use the same brush with and without the new option enabled
- use both versions without setting the origin
- switch between enabled and disabled option after setting the origin
- check whether the tooltip works

Reviewers: #krita, dkazakov

Reviewed By: #krita, dkazakov

Subscribers: dkazakov

Tags: #krita

Differential Revision:
parent 4e602f7a
......@@ -131,6 +131,11 @@ bool KisPaintOpSettings::mousePressEvent(const KisPaintInformation &paintInforma
return true; // ignore the event by default
bool KisPaintOpSettings::mouseReleaseEvent()
return true; // ignore the event by default
void KisPaintOpSettings::setRandomOffset(const KisPaintInformation &paintInformation)
if (getBool("Texture/Pattern/Enabled")) {
......@@ -86,11 +86,18 @@ public:
* This function is called by a tool when the mouse is pressed. It's useful if
* the paintop needs mouse interaction for instance in the case of the clone op.
* If the tool is supposed to ignore the event, the paint op should return false
* and if the tool is supposed to use the event, return true.
* If the tool is supposed to ignore the event, the paint op should return true
* and if the tool is supposed to use the event, return false.
* See kis_tool_freehand:tryPickByPaintOp()
virtual bool mousePressEvent(const KisPaintInformation &paintInformation, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode);
* This function is called by a tool when the mouse is released. It's useful if
* the paintop needs mouse interaction for instance in the case of the clone op.
* If the tool is supposed to ignore the event, the paint op should return true
* and if the tool is supposed to use the event, return false.
virtual bool mouseReleaseEvent();
* Clone the current settings object. Override this if your settings instance doesn't
* store everything as properties.
......@@ -188,6 +188,8 @@ void KisToolFreehand::doStroke(KoPointerEvent *event)
void KisToolFreehand::endStroke()
bool paintOpIgnoredEvent = currentPaintOpPreset()->settings()->mouseReleaseEvent();
bool KisToolFreehand::primaryActionSupportsHiResEvents() const
......@@ -285,6 +287,8 @@ bool KisToolFreehand::tryPickByPaintOp(KoPointerEvent *event, AlternateAction ac
perspective, 0, 0),
// DuplicateOP during the picking of new source point (origin)
// is the only paintop that returns "false" here
return !paintOpIgnoredEvent;
......@@ -52,6 +52,7 @@ KisDuplicateOpOption::KisDuplicateOpOption()
connect(m_optionWidget->cbHealing, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_optionWidget->cbPerspective, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_optionWidget->cbSourcePoint, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_optionWidget->cbResetSourcePoint, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
connect(m_optionWidget->chkCloneProjection, SIGNAL(toggled(bool)), SLOT(emitSettingChanged()));
......@@ -92,6 +93,16 @@ void KisDuplicateOpOption::setMoveSourcePoint(bool move)
bool KisDuplicateOpOption::resetSourcePoint() const
return m_optionWidget->cbResetSourcePoint->isChecked();
void KisDuplicateOpOption::setResetSourcePoint(bool reset)
bool KisDuplicateOpOption::cloneFromProjection() const
return m_optionWidget->chkCloneProjection->isChecked();
......@@ -109,6 +120,7 @@ void KisDuplicateOpOption::writeOptionSetting(KisPropertiesConfigurationSP setti
op.duplicate_healing = healing();
op.duplicate_correct_perspective = correctPerspective();
op.duplicate_move_source_point = moveSourcePoint();
op.duplicate_reset_source_point = resetSourcePoint();
op.duplicate_clone_from_projection = cloneFromProjection();
......@@ -122,6 +134,7 @@ void KisDuplicateOpOption::readOptionSetting(const KisPropertiesConfigurationSP
......@@ -23,6 +23,7 @@
const QString DUPLICATE_HEALING = "Duplicateop/Healing";
const QString DUPLICATE_CORRECT_PERSPECTIVE = "Duplicateop/CorrectPerspective";
const QString DUPLICATE_MOVE_SOURCE_POINT = "Duplicateop/MoveSourcePoint";
const QString DUPLICATE_RESET_SOURCE_POINT = "Duplicateop/ResetSourcePoint";
const QString DUPLICATE_CLONE_FROM_PROJECTION = "Duplicateop/CloneFromProjection";
class KisDuplicateOpOptionsWidget;
......@@ -43,6 +44,9 @@ private:
bool moveSourcePoint() const;
void setMoveSourcePoint(bool move);
bool resetSourcePoint() const;
void setResetSourcePoint(bool resetSource);
bool cloneFromProjection() const;
void setCloneFromProjection(bool cloneFromProjection);
......@@ -63,12 +67,14 @@ struct KisDuplicateOptionProperties : public KisPaintopPropertiesBase
bool duplicate_healing;
bool duplicate_correct_perspective;
bool duplicate_move_source_point;
bool duplicate_reset_source_point;
bool duplicate_clone_from_projection;
void readOptionSettingImpl(const KisPropertiesConfiguration* setting) override {
duplicate_healing = setting->getBool(DUPLICATE_HEALING, false);
duplicate_correct_perspective = setting->getBool(DUPLICATE_CORRECT_PERSPECTIVE, false);
duplicate_move_source_point = setting->getBool(DUPLICATE_MOVE_SOURCE_POINT, true);
duplicate_reset_source_point = setting->getBool(DUPLICATE_RESET_SOURCE_POINT, false);
duplicate_clone_from_projection = setting->getBool(DUPLICATE_CLONE_FROM_PROJECTION, false);
......@@ -76,6 +82,7 @@ struct KisDuplicateOptionProperties : public KisPaintopPropertiesBase
setting->setProperty(DUPLICATE_HEALING, duplicate_healing);
setting->setProperty(DUPLICATE_CORRECT_PERSPECTIVE, duplicate_correct_perspective);
setting->setProperty(DUPLICATE_MOVE_SOURCE_POINT, duplicate_move_source_point);
setting->setProperty(DUPLICATE_RESET_SOURCE_POINT, duplicate_reset_source_point);
setting->setProperty(DUPLICATE_CLONE_FROM_PROJECTION, duplicate_clone_from_projection);
......@@ -40,7 +40,7 @@
#include <kis_dom_utils.h>
: m_isOffsetNotUptodate(false)
: m_isOffsetNotUptodate(false), m_duringPaintingStroke(false)
......@@ -81,16 +81,26 @@ bool KisDuplicateOpSettings::mousePressEvent(const KisPaintInformation &info, Qt
ignoreEvent = false;
else {
if (m_isOffsetNotUptodate) {
bool resetOrigin = getBool(DUPLICATE_RESET_SOURCE_POINT);
if (m_isOffsetNotUptodate || resetOrigin) {
m_offset = info.pos() - m_position;
m_isOffsetNotUptodate = false;
m_duringPaintingStroke = true;
ignoreEvent = true;
return ignoreEvent;
bool KisDuplicateOpSettings::mouseReleaseEvent()
m_duringPaintingStroke = false;
bool ignoreEvent = true;
return ignoreEvent;
KisNodeWSP KisDuplicateOpSettings::sourceNode() const
return m_sourceNode;
......@@ -129,6 +139,7 @@ KisPaintOpSettingsSP KisDuplicateOpSettings::clone() const
s->m_isOffsetNotUptodate = m_isOffsetNotUptodate;
s->m_position = m_position;
s->m_sourceNode = m_sourceNode;
s->m_duringPaintingStroke = m_duringPaintingStroke;
return setting;
......@@ -149,7 +160,11 @@ QPainterPath KisDuplicateOpSettings::brushOutline(const KisPaintInformation &inf
QPainterPath copy(path);
QRectF rect2 = copy.boundingRect();
if (m_isOffsetNotUptodate || !getBool(DUPLICATE_MOVE_SOURCE_POINT)) {
bool shouldStayInOrigin = m_isOffsetNotUptodate // the clone brush right now waits for first stroke with a new origin, so stays at origin point
|| !getBool(DUPLICATE_MOVE_SOURCE_POINT) // the brush always use the same source point, so stays at origin point
|| (!m_duringPaintingStroke && getBool(DUPLICATE_RESET_SOURCE_POINT)); // during the stroke, with reset Origin selected, outline should stay at origin point
if (shouldStayInOrigin) {
copy.translate(m_position - info.pos());
else {
......@@ -42,7 +42,19 @@ public:
QPointF offset() const;
QPointF position() const;
* This function is called by a tool when the mouse is pressed.
* Returns false if picking new origin is in action,
* and returns true otherwise (i.e. if brush is starting a new stroke).
* See kis_tool_freehand:tryPickByPaintOp()
bool mousePressEvent(const KisPaintInformation& pos, Qt::KeyboardModifiers modifiers, KisNodeWSP currentNode) override;
* This function is called by a tool when the mouse is released.
* If the tool is supposed to ignore the event, the paint op should return true
* and if the tool is supposed to use the event, return false.
bool mouseReleaseEvent() override;
void activate() override;
void fromXML(const QDomElement& elt) override;
......@@ -61,9 +73,10 @@ public:
QPointF m_offset;
bool m_isOffsetNotUptodate;
bool m_isOffsetNotUptodate; // true between the act of setting a new origin and the first stroke
bool m_duringPaintingStroke; // true if the stroke is begin painted now, false otherwise
QPointF m_position; // Give the position of the last alt-click
KisNodeWSP m_sourceNode;
KisNodeWSP m_sourceNode; // Give the node of the source point (origin)
QList<KisUniformPaintOpPropertyWSP> m_uniformProperties;
......@@ -45,6 +45,19 @@
<widget class="QCheckBox" name="cbResetSourcePoint">
<property name="toolTip">
<string>Reset the origin every time you make a new stroke.</string>
<property name="text">
<string>Source point reset before a new stroke</string>
<property name="checked">
<widget class="QCheckBox" name="chkCloneProjection">
<property name="toolTip">
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