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

Adds move selection feature #1647

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
171 changes: 169 additions & 2 deletions src/tiled/abstracttileselectiontool.cpp
Expand Up @@ -20,12 +20,16 @@

#include "abstracttileselectiontool.h"

#include "mapdocument.h"
#include "brushitem.h"
#include "changeselectedarea.h"
#include "erasetiles.h"
#include "mapdocument.h"
#include "painttilelayer.h"

#include <QAction>
#include <QActionGroup>
#include <QApplication>
#include <QKeyEvent>
#include <QToolBar>

using namespace Tiled;
Expand All @@ -36,6 +40,9 @@ AbstractTileSelectionTool::AbstractTileSelectionTool(const QString &name,
const QKeySequence &shortcut,
QObject *parent)
: AbstractTileTool(name, icon, shortcut, parent)
, mDragModifier(false)
, mDragging(false)
, mMousePressed(false)
, mSelectionMode(Replace)
, mDefaultMode(Replace)
{
Expand Down Expand Up @@ -79,6 +86,71 @@ AbstractTileSelectionTool::AbstractTileSelectionTool(const QString &name,
languageChanged();
}

void AbstractTileSelectionTool::activate(MapScene *scene)
{
AbstractTileTool::activate(scene);
setBrushVisible(true);
}

void AbstractTileSelectionTool::deactivate(MapScene *scene)
{
if (mPreviewLayer)
anchorSelection();

AbstractTileTool::deactivate(scene);
}

void AbstractTileSelectionTool::keyPressed(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
if (mPreviewLayer) {
anchorSelection();
return;
}
break;
}

AbstractTileTool::keyPressed(event);
}

void AbstractTileSelectionTool::tilePositionChanged(const QPoint &pos)
{
if (mDragging) {
QPoint offset = pos - mLastUpdate;

if (mPreviewLayer) {
mPreviewLayer->setX(mPreviewLayer->x() + offset.x());
mPreviewLayer->setY(mPreviewLayer->y() + offset.y());
brushItem()->setTileLayer(mPreviewLayer,
brushItem()->tileRegion().translated(offset));
}

mLastUpdate = pos;
}
}

void AbstractTileSelectionTool::mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers)
{
if (mMousePressed && !mDragging) {
QPoint screenPos = QCursor::pos();
const int dragDistance = (mMouseStart - screenPos).manhattanLength();

if (dragDistance >= QApplication::startDragDistance() / 2) {
if (!mPreviewLayer)
floatSelection(modifiers & Qt::ControlModifier);

mDragging = true;
tilePositionChanged(tilePosition());
}
}

AbstractTileTool::mouseMoved(pos, modifiers);

refreshCursor();
}

void AbstractTileSelectionTool::mousePressed(QGraphicsSceneMouseEvent *event)
{
const Qt::MouseButton button = event->button();
Expand All @@ -88,6 +160,31 @@ void AbstractTileSelectionTool::mousePressed(QGraphicsSceneMouseEvent *event)

MapDocument *document = mapDocument();

if (mDragModifier || mPreviewLayer) {
if (button == Qt::LeftButton) {
bool startDrag = event->modifiers() & Qt::AltModifier;

if (!startDrag && mPreviewLayer) {
if (brushItem()->tileRegion().contains(tilePosition()))
startDrag = true;
else
anchorSelection();
}

if (startDrag) {
mMouseStart = event->screenPos();
mDragStart = tilePosition();
mMousePressed = true;
mLastUpdate = tilePosition();
}
}

if (button == Qt::RightButton)
anchorSelection();

return;
}

QRegion selection;

// Left button modifies selection, right button clears selection
Expand All @@ -108,8 +205,14 @@ void AbstractTileSelectionTool::mousePressed(QGraphicsSceneMouseEvent *event)
}
}

void AbstractTileSelectionTool::mouseReleased(QGraphicsSceneMouseEvent *)
void AbstractTileSelectionTool::mouseReleased(QGraphicsSceneMouseEvent *event)
{
if (event->button() != Qt::LeftButton)
return;

mDragging = false;
mMousePressed = false;
refreshCursor();
}

void AbstractTileSelectionTool::modifiersChanged(Qt::KeyboardModifiers modifiers)
Expand All @@ -129,6 +232,11 @@ void AbstractTileSelectionTool::modifiersChanged(Qt::KeyboardModifiers modifiers
case Subtract: mSubtract->setChecked(true); break;
case Intersect: mIntersect->setChecked(true); break;
}

mDragModifier = modifiers == Qt::AltModifier ||
modifiers == (Qt::AltModifier | Qt::ControlModifier);

refreshCursor();
}

void AbstractTileSelectionTool::languageChanged()
Expand All @@ -146,3 +254,62 @@ void AbstractTileSelectionTool::populateToolBar(QToolBar *toolBar)
toolBar->addAction(mSubtract);
toolBar->addAction(mIntersect);
}

void AbstractTileSelectionTool::refreshCursor()
{
Qt::CursorShape cursorShape = Qt::ArrowCursor;

if (mDragModifier) {
if (mPreviewLayer || !mapDocument()->selectedArea().isEmpty())
cursorShape = Qt::SizeAllCursor;
} else if (mPreviewLayer) {
if (mDragging || brushItem()->tileRegion().contains(tilePosition()))
cursorShape = Qt::SizeAllCursor;
}

if (cursor().shape() != cursorShape)
setCursor(cursorShape);
}

void AbstractTileSelectionTool::floatSelection(bool duplicate)
{
const QRegion &selectedArea = mapDocument()->selectedArea();
if (selectedArea.isEmpty())
return;

mTargetLayer = mapDocument()->currentLayer()->asTileLayer();
mPreviewLayer = SharedTileLayer(mTargetLayer->copy(selectedArea.translated(-mTargetLayer->position())));
mPreviewLayer->setPosition(selectedArea.boundingRect().topLeft());

brushItem()->setTileLayer(mPreviewLayer, selectedArea);

QUndoStack *stack = mapDocument()->undoStack();

stack->beginMacro(tr("Move Selection"));

if (!duplicate)
stack->push(new EraseTiles(mapDocument(), mTargetLayer, selectedArea));

stack->push(new ChangeSelectedArea(mapDocument(), QRegion()));

stack->endMacro();
}

void AbstractTileSelectionTool::anchorSelection()
{
const TileLayer *preview = mPreviewLayer.data();
if (!preview || !mTargetLayer)
return;

auto undoStack = mapDocument()->undoStack();

undoStack->push(new PaintTileLayer(mapDocument(),
mTargetLayer,
preview->x(),
preview->y(),
preview,
brushItem()->tileRegion()));

brushItem()->clear();
mPreviewLayer.clear();
}
31 changes: 31 additions & 0 deletions src/tiled/abstracttileselectiontool.h
Expand Up @@ -23,6 +23,8 @@

#include "abstracttiletool.h"

#include "tilelayer.h"

class QAction;
class QActionGroup;

Expand All @@ -41,6 +43,16 @@ class AbstractTileSelectionTool : public AbstractTileTool
const QKeySequence &shortcut,
QObject *parent = nullptr);

void activate(MapScene *scene) override;
void deactivate(MapScene *scene) override;

void keyPressed(QKeyEvent *event) override;

// Overridden to not show/hide the BrushItem
void mouseEntered() override {}
void mouseLeft() override {}

void mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers) override;
void mousePressed(QGraphicsSceneMouseEvent *event) override;
void mouseReleased(QGraphicsSceneMouseEvent *event) override;

Expand All @@ -51,6 +63,8 @@ class AbstractTileSelectionTool : public AbstractTileTool
void populateToolBar(QToolBar *toolBar) override;

protected:
void tilePositionChanged(const QPoint &pos) override;

enum SelectionMode {
Replace,
Add,
Expand All @@ -63,7 +77,24 @@ class AbstractTileSelectionTool : public AbstractTileTool
QRegion selectedRegion() { return mSelectedRegion; }
void setSelectedRegion(QRegion region) { mSelectedRegion = region; }

bool moving() { return mDragModifier || mPreviewLayer; }

private:
void refreshCursor();

void floatSelection(bool duplicate);
void anchorSelection();

bool mDragModifier;
bool mDragging;
bool mMousePressed;

QPoint mMouseStart;
QPoint mDragStart;
QPoint mLastUpdate;

SharedTileLayer mPreviewLayer;
TileLayer *mTargetLayer;

SelectionMode mSelectionMode;
SelectionMode mDefaultMode;
Expand Down
2 changes: 1 addition & 1 deletion src/tiled/abstracttiletool.h
Expand Up @@ -82,6 +82,7 @@ class AbstractTileTool : public AbstractTool
*/
virtual void updateStatusInfo();

void setBrushVisible(bool visible);
bool isBrushVisible() const { return mBrushVisible; }

/**
Expand Down Expand Up @@ -115,7 +116,6 @@ class AbstractTileTool : public AbstractTool
TileLayer *currentTileLayer() const;

private:
void setBrushVisible(bool visible);
void updateBrushVisibility();

TilePositionMethod mTilePositionMethod;
Expand Down
5 changes: 5 additions & 0 deletions src/tiled/magicwandtool.cpp
Expand Up @@ -40,6 +40,11 @@ MagicWandTool::MagicWandTool(QObject *parent)

void MagicWandTool::tilePositionChanged(const QPoint &tilePos)
{
AbstractTileSelectionTool::tilePositionChanged(tilePos);

if (moving())
return;

// Make sure that a tile layer is selected
TileLayer *tileLayer = currentTileLayer();
if (!tileLayer)
Expand Down
5 changes: 5 additions & 0 deletions src/tiled/selectsametiletool.cpp
Expand Up @@ -36,6 +36,11 @@ SelectSameTileTool::SelectSameTileTool(QObject *parent)

void SelectSameTileTool::tilePositionChanged(const QPoint &tilePos)
{
AbstractTileSelectionTool::tilePositionChanged(tilePos);

if (moving())
return;

// Make sure that a tile layer is selected and contains current tile pos.
TileLayer *tileLayer = currentTileLayer();
if (!tileLayer)
Expand Down
51 changes: 33 additions & 18 deletions src/tiled/tileselectiontool.cpp
Expand Up @@ -44,8 +44,10 @@ TileSelectionTool::TileSelectionTool(QObject *parent)
setTilePositionMethod(OnTiles);
}

void TileSelectionTool::tilePositionChanged(const QPoint &)
void TileSelectionTool::tilePositionChanged(const QPoint &pos)
{
AbstractTileSelectionTool::tilePositionChanged(pos);

if (mSelecting)
brushItem()->setTileRegion(selectedArea());
}
Expand Down Expand Up @@ -77,34 +79,47 @@ void TileSelectionTool::mouseMoved(const QPointF &pos, Qt::KeyboardModifiers mod
}
}

AbstractTileTool::mouseMoved(pos, modifiers);
AbstractTileSelectionTool::mouseMoved(pos, modifiers);
}

void TileSelectionTool::mousePressed(QGraphicsSceneMouseEvent *event)
{
const Qt::MouseButton button = event->button();

if (button == Qt::LeftButton) {
mMouseDown = true;
mMouseScreenStart = event->screenPos();
mSelectionStart = tilePosition();
brushItem()->setTileRegion(QRegion());
}

if (button == Qt::RightButton) {
if (mSelecting) {
// Cancel selecting
mSelecting = false;
mMouseDown = false; // Avoid restarting select on move
if (selectionMode() == Intersect)
setSelectedRegion(mapDocument()->selectedArea());
else
setSelectedRegion(QRegion());
AbstractTileSelectionTool::mousePressed(event);

if (!moving()) {
const Qt::MouseButton button = event->button();

if (button == Qt::LeftButton) {
mMouseDown = true;
mMouseScreenStart = event->screenPos();
mSelectionStart = tilePosition();
brushItem()->setTileRegion(QRegion());
} else {
clearSelection();
}

if (button == Qt::RightButton) {
if (mSelecting) {
// Cancel selecting
mSelecting = false;
mMouseDown = false; // Avoid restarting select on move
brushItem()->setTileRegion(QRegion());
} else {
clearSelection();
}
}
}
}

void TileSelectionTool::mouseReleased(QGraphicsSceneMouseEvent *event)
{
AbstractTileSelectionTool::mouseReleased(event);

if (moving())
return;

if (event->button() != Qt::LeftButton)
return;

Expand Down