Skip to content

Commit

Permalink
Stamp Brush right-click cut when Shift is held (#3963)
Browse files Browse the repository at this point in the history
Implementing this required a refactor of the state system in
StampBrush.  Behavior and State were previously handled in the
same property, which resulted both in wrong behavior when using
capture and pressing the behavior keys (replicate by holding
right click, start holding shift, release right click, the behavior
wasn't set to line as it should have). This commit refactors this
system by splitting State and Behavior in two enums and two
properties, resulting in making the implemented feature possible,
fixing behavior bugs, and future-proofing this system for
expansion.

Implements #3961

Co-authored-by: Thorbjørn Lindeijer <[email protected]>
  • Loading branch information
kdx2a and bjorn authored Jun 14, 2024
1 parent 0bc36ee commit 9de4b79
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 131 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Added --project command-line parameter for use when exporting (#3797)
* Added group layer names in "Move Object to Layer" menu (#3454)
* Added lock icon to open tabs for which the file is read-only
* Added Shift modifier to cut when capturing a tile stamp (by kdx2a, #3961)
* Made adding "Copy" when duplicating optional and disabled by default (#3917)
* Layer names are now trimmed when edited in the UI, to avoid accidental whitespace
* Scripting: Added API for working with worlds (#3539)
Expand Down
7 changes: 5 additions & 2 deletions src/tiled/abstracttilefilltool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ void AbstractTileFillTool::deactivate(MapScene *scene)

void AbstractTileFillTool::mousePressed(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::RightButton && event->modifiers() == Qt::NoModifier) {
if (event->button() == Qt::RightButton &&
!(event->modifiers() & Qt::ControlModifier))
{
mCaptureStampHelper.beginCapture(tilePosition());
return;
}
Expand All @@ -89,7 +91,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
10 changes: 9 additions & 1 deletion src/tiled/capturestamphelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,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 +55,14 @@ TileStamp CaptureStampHelper::endCapture(const MapDocument &mapDocument, QPoint
captured,
*stamp);

// Erase captured area when cutting
if (cut && !captured.isEmpty()) {
const bool allLayers = false;
const bool mergeable = false;
mapDocument.eraseTileLayers(captured, allLayers, mergeable,
Document::tr("Cut"));
}

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
42 changes: 1 addition & 41 deletions src/tiled/eraser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@

#include "brushitem.h"
#include "geometry.h"
#include "map.h"
#include "mapdocument.h"
#include "mapscene.h"
#include "painttilelayer.h"
#include "tilelayer.h"

#include <QCoreApplication>

Expand Down Expand Up @@ -111,45 +109,7 @@ void Eraser::doErase(bool continuation)
}
mLastTilePos = tilePos;

QList<QPair<QRegion, TileLayer*>> erasedRegions;

auto *eraseCommand = new PaintTileLayer(mapDocument());
eraseCommand->setText(QCoreApplication::translate("Undo Commands", "Erase"));
eraseCommand->setMergeable(continuation);

auto eraseOnLayer = [&] (TileLayer *tileLayer) {
if (!tileLayer->isUnlocked())
return;

QRegion eraseRegion = globalEraseRegion.intersected(tileLayer->bounds());
if (eraseRegion.isEmpty())
return;

eraseCommand->erase(tileLayer, eraseRegion);

erasedRegions.append({ eraseRegion, tileLayer });
};

if (mAllLayers) {
for (Layer *layer : mapDocument()->map()->tileLayers())
eraseOnLayer(static_cast<TileLayer*>(layer));
} else if (!mapDocument()->selectedLayers().isEmpty()) {
for (Layer *layer : mapDocument()->selectedLayers())
if (TileLayer *tileLayer = layer->asTileLayer())
eraseOnLayer(tileLayer);
} else if (auto tileLayer = currentTileLayer()) {
eraseOnLayer(tileLayer);
}

if (!erasedRegions.isEmpty())
mapDocument()->undoStack()->push(eraseCommand);

for (auto &[region, tileLayer] : std::as_const(erasedRegions)) {
if (tileLayer->map() != mapDocument()->map())
continue;

emit mapDocument()->regionEdited(region, tileLayer);
}
mapDocument()->eraseTileLayers(globalEraseRegion, mAllLayers, continuation);
}

QRect Eraser::eraseArea() const
Expand Down
49 changes: 49 additions & 0 deletions src/tiled/mapdocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,55 @@ void MapDocument::paintTileLayers(const Map &map, bool mergeable,
}
}

void MapDocument::eraseTileLayers(const QRegion &region,
bool allLayers,
bool mergeable,
const QString &customName)
{
QList<QPair<QRegion, TileLayer*>> erasedRegions;

auto eraseCommand = std::make_unique<PaintTileLayer>(this);
eraseCommand->setText(customName.isEmpty() ? QCoreApplication::translate("Undo Commands", "Erase")
: customName);
eraseCommand->setMergeable(mergeable);

auto eraseOnLayer = [&] (TileLayer *tileLayer) {
if (!tileLayer->isUnlocked())
return;

QRegion eraseRegion = region.intersected(tileLayer->bounds());
if (eraseRegion.isEmpty())
return;

eraseCommand->erase(tileLayer, eraseRegion);

erasedRegions.append({ eraseRegion, tileLayer });
};

if (allLayers) {
for (Layer *layer : map()->tileLayers())
eraseOnLayer(static_cast<TileLayer*>(layer));
} else if (!selectedLayers().isEmpty()) {
for (Layer *layer : selectedLayers())
if (TileLayer *tileLayer = layer->asTileLayer())
eraseOnLayer(tileLayer);
} else if (auto tileLayer = dynamic_cast<TileLayer*>(currentLayer())) {
eraseOnLayer(tileLayer);
}

if (!erasedRegions.isEmpty())
undoStack()->push(eraseCommand.release());

for (auto &[region, tileLayer] : std::as_const(erasedRegions)) {
// Sanity check needed because a script might respond to the below
// signal by removing the layer from the map.
if (tileLayer->map() != map())
continue;

emit regionEdited(region, tileLayer);
}
}

void MapDocument::replaceObjectTemplate(const ObjectTemplate *oldObjectTemplate,
const ObjectTemplate *newObjectTemplate)
{
Expand Down
4 changes: 4 additions & 0 deletions src/tiled/mapdocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ class TILED_EDITOR_EXPORT MapDocument final : public Document
void paintTileLayers(const Map &map, bool mergeable = false,
QVector<SharedTileset> *missingTilesets = nullptr,
QHash<TileLayer *, QRegion> *paintedRegions = nullptr);
void eraseTileLayers(const QRegion &region,
bool allLayers = false,
bool mergeable = false,
const QString &customName = QString());

void replaceObjectTemplate(const ObjectTemplate *oldObjectTemplate,
const ObjectTemplate *newObjectTemplate);
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/mapdocumentactionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ void MapDocumentActionHandler::delete_()
}

for (auto &erased : std::as_const(erasedRegions)) {
// Sanity check needed because a script might respond to the below
// signal by removing the layer from the map.
if (erased.second->map() != mMapDocument->map())
continue;

Expand Down
Loading

0 comments on commit 9de4b79

Please sign in to comment.