Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stamp Brush right-click cut when Shift is held #3963

Merged
merged 9 commits into from
Jun 14, 2024
5 changes: 3 additions & 2 deletions src/tiled/abstracttilefilltool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void AbstractTileFillTool::deactivate(MapScene *scene)

void AbstractTileFillTool::mousePressed(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) {
if (event->button() == Qt::RightButton) {
mCaptureStampHelper.beginCapture(tilePosition());
return;
}
Expand All @@ -89,7 +89,8 @@ void AbstractTileFillTool::mouseReleased(QGraphicsSceneMouseEvent *event)
if (event->button() == Qt::RightButton && mCaptureStampHelper.isActive()) {
clearOverlay();

TileStamp stamp = mCaptureStampHelper.endCapture(*mapDocument(), tilePosition());
const bool cut = event->modifiers() & Qt::ShiftModifier;
TileStamp stamp = mCaptureStampHelper.endCapture(*mapDocument(), tilePosition(), cut);
if (!stamp.isEmpty())
emit stampChanged(stamp);

Expand Down
28 changes: 27 additions & 1 deletion src/tiled/capturestamphelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@

#include "capturestamphelper.h"

#include "erasetiles.h"
#include "map.h"
#include "mapdocument.h"
#include "tilelayer.h"

#include <memory>

Expand All @@ -38,7 +40,7 @@ void CaptureStampHelper::beginCapture(QPoint tilePosition)
mCaptureStart = tilePosition;
}

TileStamp CaptureStampHelper::endCapture(const MapDocument &mapDocument, QPoint tilePosition)
TileStamp CaptureStampHelper::endCapture(MapDocument &mapDocument, QPoint tilePosition, bool cut)
{
mActive = false;

Expand All @@ -55,6 +57,30 @@ TileStamp CaptureStampHelper::endCapture(const MapDocument &mapDocument, QPoint
captured,
*stamp);

// Delete captured elements when cutting
if (cut && !captured.isEmpty()) {
QList<QUndoCommand*> commands;
QList<QPair<QRegion, TileLayer*>> erasedRegions;

for (auto layer : mapDocument.selectedLayers()) {
if (!layer->isTileLayer())
continue;
TileLayer *const tileLayer = layer->asTileLayer();
const QRegion area = captured.intersected(tileLayer->bounds());
if (area.isEmpty())
continue;
// Delete the captured part of the layer
commands.append(new EraseTiles(&mapDocument, tileLayer, area));
erasedRegions.append({ area, tileLayer });
}

QUndoStack *const undoStack = mapDocument.undoStack();
undoStack->beginMacro(Document::tr("Cut"));
for (QUndoCommand *command : std::as_const(commands))
undoStack->push(command);
undoStack->endMacro();
}

if (stamp->layerCount() > 0) {
stamp->normalizeTileLayerPositionsAndMapSize();

Expand Down
2 changes: 1 addition & 1 deletion src/tiled/capturestamphelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class CaptureStampHelper
CaptureStampHelper();

void beginCapture(QPoint tilePosition);
TileStamp endCapture(const MapDocument &mapDocument, QPoint tilePosition);
TileStamp endCapture(MapDocument &mapDocument, QPoint tilePosition, bool cut);

bool isActive() const { return mActive; }
void reset();
Expand Down
142 changes: 64 additions & 78 deletions src/tiled/stampbrush.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,16 @@ void StampBrush::activate(MapScene *scene)

void StampBrush::deactivate(MapScene *scene)
{
mBrushBehavior = Free;
mBrushBehavior = BrushBehavior::Neutral;
mBrushState = BrushState::Free;
mCaptureStampHelper.reset();
mStampActions->setEnabled(false);
AbstractTileTool::deactivate(scene);
}

void StampBrush::tilePositionChanged(QPoint pos)
{
if (mBrushBehavior == Paint) {
if (mBrushState == BrushState::Paint) {
// Draw a line from the previous point to avoid gaps, skipping the
// first point, since it was painted when the mouse was pressed, or the
// last time the mouse was moved.
Expand Down Expand Up @@ -117,33 +118,31 @@ void StampBrush::mousePressed(QGraphicsSceneMouseEvent *event)
{
if (brushItem()->isVisible()) {
if (event->button() == Qt::LeftButton) {
switch (mBrushBehavior) {
case Line:
mStampReference = tilePosition();
mBrushBehavior = LineStartSet;
break;
case Circle:
mStampReference = tilePosition();
mBrushBehavior = CircleMidSet;
break;
case LineStartSet:
switch (mBrushState) {
case BrushState::StartSet:
doPaint();
mStampReference = tilePosition();
break;
case CircleMidSet:
doPaint();
break;
case Paint:
case BrushState::Paint:
beginPaint();
break;
case Free:
beginPaint();
case BrushState::Free:
switch (mBrushBehavior) {
case BrushBehavior::Neutral:
beginPaint();
break;
case BrushBehavior::Line:
case BrushBehavior::Circle:
mStampReference = tilePosition();
mBrushState = BrushState::StartSet;
break;
}
break;
case Capture:
case BrushState::Capture:
break;
}
return;
} else if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) {
} else if (event->button() == Qt::RightButton) {
beginCapture();
return;
}
Expand All @@ -154,32 +153,24 @@ void StampBrush::mousePressed(QGraphicsSceneMouseEvent *event)

void StampBrush::mouseReleased(QGraphicsSceneMouseEvent *event)
{
switch (mBrushBehavior) {
case LineStartSet:
switch (mBrushState) {
case BrushState::StartSet:
if (event->button() == Qt::LeftButton) {
if (mStampReference != tilePosition()) {
doPaint();
mBrushBehavior = Line;
mBrushState = BrushState::Free;
}
}
break;
case CircleMidSet:
if (event->button() == Qt::LeftButton) {
if (mStampReference != tilePosition()) {
doPaint();
updateBrushBehavior();
}
}
break;
case Capture:
case BrushState::Capture:
if (event->button() == Qt::RightButton) {
endCapture();
mBrushBehavior = Free;
mBrushState = BrushState::Free;
}
break;
case Paint:
case BrushState::Paint:
if (event->button() == Qt::LeftButton) {
mBrushBehavior = Free;
mBrushState = BrushState::Free;

// allow going over different variations by repeatedly clicking
updatePreview();
Expand All @@ -194,35 +185,28 @@ void StampBrush::mouseReleased(QGraphicsSceneMouseEvent *event)
void StampBrush::modifiersChanged(Qt::KeyboardModifiers modifiers)
{
mModifiers = modifiers;

if (!mStamp.isEmpty() || mIsWangFill)
updateBrushBehavior();
updateBrushBehavior();
}

void StampBrush::updateBrushBehavior()
{
BrushBehavior brushBehavior = mBrushBehavior;
BrushState brushState = mBrushState;

if (mModifiers & Qt::ShiftModifier) {
if (mModifiers & Qt::ControlModifier) {
if (brushBehavior == LineStartSet) {
brushBehavior = CircleMidSet;
} else if (brushBehavior != CircleMidSet) {
brushBehavior = Circle;
}
} else {
if (brushBehavior == CircleMidSet) {
brushBehavior = LineStartSet;
} else if (brushBehavior != LineStartSet) {
brushBehavior = Line;
}
}
} else if (brushBehavior != Paint && brushBehavior != Capture) {
brushBehavior = Free;
if (mModifiers & Qt::ControlModifier)
brushBehavior = BrushBehavior::Circle;
else
brushBehavior = BrushBehavior::Line;
} else {
brushBehavior = BrushBehavior::Neutral;
if (brushState == BrushState::StartSet)
brushState = BrushState::Free;
}

if (mBrushBehavior != brushBehavior) {
if (brushBehavior != mBrushBehavior || brushState != mBrushState) {
mBrushBehavior = brushBehavior;
mBrushState = brushState;
updatePreview();
}
}
Expand Down Expand Up @@ -316,32 +300,33 @@ void StampBrush::setWangSet(WangSet *wangSet)

void StampBrush::beginPaint()
{
if (mBrushBehavior != Free)
if (mBrushState != BrushState::Free)
return;

mBrushBehavior = Paint;
mBrushState = BrushState::Paint;
doPaint();
}

void StampBrush::beginCapture()
{
if (mBrushBehavior != Free)
if (mBrushState != BrushState::Free)
return;

mBrushBehavior = Capture;
mBrushState = BrushState::Capture;
mCaptureStampHelper.beginCapture(tilePosition());

setStamp(TileStamp());
}

void StampBrush::endCapture()
{
if (mBrushBehavior != Capture)
if (mBrushState != BrushState::Capture)
return;

mBrushBehavior = Free;
mBrushState = BrushState::Free;

TileStamp stamp = mCaptureStampHelper.endCapture(*mapDocument(), tilePosition());
const bool cut = mModifiers & Qt::ShiftModifier;
TileStamp stamp = mCaptureStampHelper.endCapture(*mapDocument(), tilePosition(), cut);
if (!stamp.isEmpty())
emit stampChanged(TileStamp(stamp));
else
Expand Down Expand Up @@ -609,31 +594,32 @@ void StampBrush::updatePreview(QPoint tilePos)

QRegion tileRegion;

if (mBrushBehavior == Capture) {
if (mBrushState == BrushState::Capture) {
mPreviewMap.clear();
tileRegion = mCaptureStampHelper.capturedArea(tilePos);
} else {
switch (mBrushBehavior) {
case LineStartSet:
drawPreviewLayer(pointsOnLine(mStampReference, tilePos));
break;
case CircleMidSet:
drawPreviewLayer(pointsOnEllipse(mStampReference,
qAbs(mStampReference.x() - tilePos.x()),
qAbs(mStampReference.y() - tilePos.y())));
switch (mBrushState) {
case BrushState::StartSet:
if (mBrushBehavior == BrushBehavior::Circle) {
drawPreviewLayer(pointsOnEllipse(mStampReference,
qAbs(mStampReference.x() - tilePos.x()),
qAbs(mStampReference.y() - tilePos.y())));
} else {
drawPreviewLayer(pointsOnLine(mStampReference, tilePos));
}
break;
case Capture:
case BrushState::Capture:
// already handled above
break;
case Circle:
case BrushState::Free:
case BrushState::Paint:
// while finding the mid point, there is no need to show
// the (maybe bigger than 1x1) stamp
mPreviewMap.clear();
break;
case Line:
case Free:
case Paint:
drawPreviewLayer(QVector<QPoint>() << tilePos);
if (mBrushBehavior == BrushBehavior::Circle) {
mPreviewMap.clear();
} else {
drawPreviewLayer(QVector<QPoint>() << tilePos);
}
break;
}

Expand Down
18 changes: 11 additions & 7 deletions src/tiled/stampbrush.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,22 +124,26 @@ public slots:
* There are several options how the stamp utility can be used.
* It must be one of the following:
*/
enum BrushBehavior {
enum class BrushBehavior {
Neutral, // nothing special
Line, // hold shift: a line
Circle // hold Shift + Ctrl: a circle
};

enum class BrushState {
Free, // nothing special: you can move the mouse,
// preview of the selection
Paint, // left mouse pressed: free painting
Capture, // right mouse pressed: capture a rectangle
Line, // hold shift: a line
LineStartSet, // when you have defined a starting point,
Paint, // left mouse pressed: free painting
StartSet // when you have defined a starting point,
// cancel with right click
Circle, // hold Shift + Ctrl: a circle
CircleMidSet
};

/**
* This stores the current behavior.
*/
BrushBehavior mBrushBehavior = Free;
BrushBehavior mBrushBehavior = BrushBehavior::Neutral;
BrushState mBrushState = BrushState::Free;
Qt::KeyboardModifiers mModifiers;

/**
Expand Down
Loading