2323#include " brushitem.h"
2424#include " changeselectedarea.h"
2525#include " mapdocument.h"
26+ #include " mapscene.h"
27+ #include " movetiles.h"
28+ #include " tilelayer.h"
2629
2730#include < QAction>
2831#include < QActionGroup>
2932#include < QApplication>
33+ #include < QGraphicsView>
3034#include < QKeyEvent>
3135#include < QToolBar>
36+ #include < QUndoStack>
3237
3338using namespace Tiled ;
3439
@@ -81,34 +86,68 @@ AbstractTileSelectionTool::AbstractTileSelectionTool(Id id,
8186 AbstractTileSelectionTool::languageChanged ();
8287}
8388
89+ AbstractTileSelectionTool::~AbstractTileSelectionTool ()
90+ {
91+ }
92+
93+ void AbstractTileSelectionTool::deactivate (MapScene *scene)
94+ {
95+ if (mMoveState == MovingTiles)
96+ cancelMove ();
97+
98+ AbstractTileTool::deactivate (scene);
99+ }
100+
84101void AbstractTileSelectionTool::mousePressed (QGraphicsSceneMouseEvent *event)
85102{
86103 const auto button = event->button ();
87104 const auto modifiers = event->modifiers ();
88105
89106 if (button == Qt::LeftButton) {
107+ if (tryStartMove (event))
108+ return ;
109+
90110 mMouseDown = true ;
91111 return ;
92112 }
93113
94- if (button == Qt::RightButton && modifiers == Qt::NoModifier) {
95- // Right mouse button cancels selection
96- if (mMouseDown ) {
97- mMouseDown = false ;
98- setSelectionPreview (QRegion ());
114+ if (button == Qt::RightButton) {
115+ if (mMoveState == MovingTiles) {
116+ cancelMove ();
99117 return ;
100118 }
101119
102- // Right mouse button clears selection
103- changeSelectedArea (QRegion ());
104- return ;
120+ if (modifiers == Qt::NoModifier) {
121+ if (mMouseDown ) {
122+ mMouseDown = false ;
123+ setSelectionPreview (QRegion ());
124+ return ;
125+ }
126+
127+ changeSelectedArea (QRegion ());
128+ return ;
129+ }
105130 }
106131
107132 AbstractTileTool::mousePressed (event);
108133}
109134
110135void AbstractTileSelectionTool::mouseReleased (QGraphicsSceneMouseEvent *event)
111136{
137+ if (event->button () == Qt::LeftButton) {
138+ switch (mMoveState ) {
139+ case PickingUp:
140+ mMoveState = NoMove;
141+ updateMoveCursor ();
142+ return ;
143+ case MovingTiles:
144+ commitMove ();
145+ return ;
146+ case NoMove:
147+ break ;
148+ }
149+ }
150+
112151 if (event->button () == Qt::LeftButton && mMouseDown ) {
113152 mMouseDown = false ;
114153
@@ -119,8 +158,33 @@ void AbstractTileSelectionTool::mouseReleased(QGraphicsSceneMouseEvent *event)
119158 }
120159}
121160
161+ void AbstractTileSelectionTool::mouseMoved (const QPointF &pos,
162+ Qt::KeyboardModifiers modifiers)
163+ {
164+ AbstractTileTool::mouseMoved (pos, modifiers);
165+
166+ if (mMoveState == PickingUp) {
167+ QPointF screenPos = mapScene ()->views ().first ()->mapFromScene (pos);
168+ QPointF delta = screenPos - mMoveScreenStart ;
169+ if (delta.manhattanLength () >= MoveDragThreshold) {
170+ pickUpSelection ();
171+ }
172+ } else if (mMoveState == MovingTiles) {
173+ updateFloatingPosition ();
174+ }
175+ }
176+
122177void AbstractTileSelectionTool::modifiersChanged (Qt::KeyboardModifiers modifiers)
123178{
179+ if (mMoveState == MovingTiles) {
180+ bool wasDuplicate = mDuplicateMode ;
181+ mDuplicateMode = modifiers & Qt::AltModifier;
182+ if (wasDuplicate != mDuplicateMode )
183+ updateStatusInfo ();
184+ updateMoveCursor ();
185+ return ;
186+ }
187+
124188 if (modifiers == Qt::ControlModifier)
125189 mSelectionMode = Subtract;
126190 else if (modifiers == Qt::ShiftModifier)
@@ -141,8 +205,12 @@ void AbstractTileSelectionTool::modifiersChanged(Qt::KeyboardModifiers modifiers
141205void AbstractTileSelectionTool::keyPressed (QKeyEvent *event)
142206{
143207 if (event->key () == Qt::Key_Escape) {
208+ if (mMoveState == MovingTiles) {
209+ cancelMove ();
210+ return ;
211+ }
212+
144213 if (mMouseDown ) {
145- // Cancel the ongoing selection
146214 mMouseDown = false ;
147215 setSelectionPreview (QRegion ());
148216 return ;
@@ -212,4 +280,188 @@ void AbstractTileSelectionTool::updateBrushVisibility()
212280 brushItem ()->setVisible (isBrushVisible ());
213281}
214282
283+ void AbstractTileSelectionTool::mapDocumentChanged (MapDocument *oldDocument,
284+ MapDocument *newDocument)
285+ {
286+ if (mMoveState == MovingTiles)
287+ cancelMove ();
288+
289+ if (oldDocument) {
290+ disconnect (oldDocument, &MapDocument::selectedAreaChanged,
291+ this , &AbstractTileSelectionTool::onMoveSelectionChanged);
292+ }
293+
294+ if (newDocument) {
295+ connect (newDocument, &MapDocument::selectedAreaChanged,
296+ this , &AbstractTileSelectionTool::onMoveSelectionChanged);
297+ }
298+
299+ AbstractTileTool::mapDocumentChanged (oldDocument, newDocument);
300+ }
301+
302+ bool AbstractTileSelectionTool::tryStartMove (QGraphicsSceneMouseEvent *event)
303+ {
304+ if (mMoveState != NoMove)
305+ return true ;
306+
307+ const auto modifiers = event->modifiers ();
308+
309+ // Shift/Ctrl are selection mode modifiers -- don't intercept
310+ if (modifiers & (Qt::ShiftModifier | Qt::ControlModifier))
311+ return false ;
312+
313+ if (!hasActiveSelection ())
314+ return false ;
315+
316+ if (!mapDocument ()->selectedArea ().contains (tilePosition ()))
317+ return false ;
318+
319+ mMoveScreenStart = event->screenPos ();
320+ mPickupTilePos = tilePosition ();
321+ mDuplicateMode = modifiers & Qt::AltModifier;
322+ mMoveState = PickingUp;
323+ updateMoveCursor ();
324+ return true ;
325+ }
326+
327+ void AbstractTileSelectionTool::pickUpSelection ()
328+ {
329+ if (!mapDocument ())
330+ return ;
331+
332+ TileLayer *layer = currentTileLayer ();
333+ if (!layer)
334+ return ;
335+
336+ mOriginalSelection = mapDocument ()->selectedArea ();
337+ if (mOriginalSelection .isEmpty ()) {
338+ mMoveState = NoMove;
339+ updateMoveCursor ();
340+ return ;
341+ }
342+
343+ mFloatingTiles = SharedTileLayer::create ();
344+ mFloatingTiles ->setCells (0 , 0 , layer, mOriginalSelection );
345+
346+ brushItem ()->setTileLayer (mFloatingTiles , mOriginalSelection );
347+ brushItem ()->setAnimatedOutline (true );
348+ brushItem ()->setTileOffset (QPoint (0 , 0 ));
349+
350+ mUndoIndexBeforeMove = mapDocument ()->undoStack ()->index ();
351+ connect (mapDocument ()->undoStack (), &QUndoStack::indexChanged,
352+ this , &AbstractTileSelectionTool::onUndoIndexChanged);
353+
354+ mCurrentTilePos = mPickupTilePos ;
355+ mMoveState = MovingTiles;
356+ updateMoveCursor ();
357+ }
358+
359+ void AbstractTileSelectionTool::updateFloatingPosition ()
360+ {
361+ QPoint offset = tilePosition () - mPickupTilePos ;
362+ brushItem ()->setTileOffset (offset);
363+ mCurrentTilePos = tilePosition ();
364+ }
365+
366+ void AbstractTileSelectionTool::commitMove ()
367+ {
368+ if (!mapDocument () || mOriginalSelection .isEmpty ()) {
369+ cancelMove ();
370+ return ;
371+ }
372+
373+ TileLayer *layer = currentTileLayer ();
374+ if (!layer) {
375+ cancelMove ();
376+ return ;
377+ }
378+
379+ disconnect (mapDocument ()->undoStack (), &QUndoStack::indexChanged,
380+ this , &AbstractTileSelectionTool::onUndoIndexChanged);
381+
382+ QPoint offset = mCurrentTilePos - mPickupTilePos ;
383+
384+ if (offset != QPoint (0 , 0 ) || mDuplicateMode ) {
385+ QUndoStack *undoStack = mapDocument ()->undoStack ();
386+
387+ undoStack->beginMacro (mDuplicateMode ? tr (" Duplicate Tiles" ) : tr (" Move Tiles" ));
388+
389+ undoStack->push (new MoveTiles (
390+ mapDocument (),
391+ layer,
392+ mOriginalSelection ,
393+ offset,
394+ mDuplicateMode
395+ ));
396+
397+ QRegion newSelection = mOriginalSelection .translated (offset);
398+ undoStack->push (new ChangeSelectedArea (mapDocument (), newSelection));
399+
400+ undoStack->endMacro ();
401+ }
402+
403+ brushItem ()->setAnimatedOutline (false );
404+ brushItem ()->setTileOffset (QPoint (0 , 0 ));
405+ brushItem ()->setTileLayer (SharedTileLayer ());
406+ mFloatingTiles .reset ();
407+ mOriginalSelection = QRegion ();
408+
409+ mMoveState = NoMove;
410+ updateMoveCursor ();
411+ }
412+
413+ void AbstractTileSelectionTool::cancelMove ()
414+ {
415+ if (mapDocument ()) {
416+ disconnect (mapDocument ()->undoStack (), &QUndoStack::indexChanged,
417+ this , &AbstractTileSelectionTool::onUndoIndexChanged);
418+ }
419+
420+ brushItem ()->setAnimatedOutline (false );
421+ brushItem ()->setTileOffset (QPoint (0 , 0 ));
422+ brushItem ()->setTileLayer (SharedTileLayer ());
423+ mFloatingTiles .reset ();
424+ mOriginalSelection = QRegion ();
425+
426+ mMoveState = NoMove;
427+ updateMoveCursor ();
428+ }
429+
430+ bool AbstractTileSelectionTool::hasActiveSelection () const
431+ {
432+ return mapDocument () && !mapDocument ()->selectedArea ().isEmpty ();
433+ }
434+
435+ void AbstractTileSelectionTool::updateMoveCursor ()
436+ {
437+ switch (mMoveState ) {
438+ case NoMove:
439+ setCursor (QCursor ());
440+ break ;
441+ case PickingUp:
442+ setCursor (Qt::ClosedHandCursor);
443+ break ;
444+ case MovingTiles:
445+ setCursor (mDuplicateMode ? Qt::DragCopyCursor : Qt::SizeAllCursor);
446+ break ;
447+ }
448+ }
449+
450+ void AbstractTileSelectionTool::onUndoIndexChanged ()
451+ {
452+ if (mMoveState == MovingTiles && mapDocument ()) {
453+ int currentIndex = mapDocument ()->undoStack ()->index ();
454+ if (currentIndex < mUndoIndexBeforeMove ) {
455+ cancelMove ();
456+ }
457+ }
458+ }
459+
460+ void AbstractTileSelectionTool::onMoveSelectionChanged ()
461+ {
462+ if (mMoveState == MovingTiles) {
463+ cancelMove ();
464+ }
465+ }
466+
215467#include " moc_abstracttileselectiontool.cpp"
0 commit comments