Multi-command paste and move operations - Start, Move, Finish are recorded in history

...and add some type info
This commit is contained in:
David Vierra 2015-01-08 23:47:40 -10:00
parent 98ff85b544
commit b4e1c43f9a
7 changed files with 136 additions and 45 deletions

View File

@ -43,11 +43,12 @@ class SimpleRevisionCommand(QtGui.QUndoCommand):
self.setText(text)
def undo(self):
self.editorSession.gotoRevision(self.previousRevision)
if self.previousRevision is not None:
self.editorSession.gotoRevision(self.previousRevision)
def redo(self):
self.editorSession.gotoRevision(self.currentRevision)
pass
if self.currentRevision is not None:
self.editorSession.gotoRevision(self.currentRevision)
@contextlib.contextmanager
def begin(self):

View File

@ -47,7 +47,9 @@ class EditorSession(QtCore.QObject):
self.undoBlock = None
self.currentTool = None
self.dirty = False
self.copiedSchematic = None
""":type : WorldEditor"""
self.versionInfo = versionInfo
# --- Open world editor ---
@ -241,10 +243,11 @@ class EditorSession(QtCore.QObject):
return
moveTool = self.tools["Move"]
if moveTool is self.currentTool:
moveTool.completeMove()
moveTool.movingSchematic = self.copiedSchematic
moveTool.movePosition = self.editorTab.currentView().viewCenter()
ray = self.editorTab.currentView().rayAtCenter()
point = ray.point + ray.vector * (self.copiedSchematic.getDimension().bounds.size.length() * 2)
point = Vector(*[int(i) for i in point])
moveTool.pasteSchematic(self.copiedSchematic, point)
self.chooseTool("Move")
def pasteBlocks(self):
@ -271,7 +274,7 @@ class EditorSession(QtCore.QObject):
self.undoStack.removeUndoBlock(callback)
def beginUndo(self):
self.undoStack.clearUndoBlock(True)
self.undoStack.clearUndoBlock()
self.dirty = True
self.worldEditor.beginUndo()
@ -544,7 +547,7 @@ class EditorTab(QtGui.QWidget):
def currentView(self):
"""
:rtype: WorldView
:rtype: mcedit2.worldview.worldview.WorldView
"""
return self.viewStack.currentWidget().worldView

View File

@ -21,6 +21,65 @@ from mceditlib.geometry import BoundingBox, Vector
log = logging.getLogger(__name__)
class MoveSelectionCommand(SimpleRevisionCommand):
def __init__(self, moveTool, movingSchematic, movePosition=None, text=None, *args, **kwargs):
if text is None:
text = moveTool.tr("Move Selected Blocks")
if movePosition is None:
movePosition = moveTool.editorSession.currentSelection.origin
super(MoveSelectionCommand, self).__init__(moveTool.editorSession, text, *args, **kwargs)
self.moveTool = moveTool
self.movingSchematic = movingSchematic
self.movePosition = movePosition
def undo(self):
super(MoveSelectionCommand, self).undo()
self.moveTool.movingSchematic = None
self.moveTool.movePosition = None
self.moveTool.editorSession.chooseTool("Select")
def redo(self):
self.moveTool.movingSchematic = self.movingSchematic
self.moveTool.movePosition = self.movePosition
self.moveTool.editorSession.chooseTool("Move")
super(MoveSelectionCommand, self).redo()
class MoveOffsetCommand(QtGui.QUndoCommand):
def __init__(self, moveTool, oldPoint, newPoint):
super(MoveOffsetCommand, self).__init__()
self.setText(moveTool.tr("Move Selection"))
self.newPoint = newPoint
self.oldPoint = oldPoint
self.moveTool = moveTool
def undo(self):
self.moveTool.movePosition = self.oldPoint
def redo(self):
self.moveTool.movePosition = self.newPoint
class MoveFinishCommand(SimpleRevisionCommand):
def __init__(self, moveTool, movingSchematic, *args, **kwargs):
super(MoveFinishCommand, self).__init__(moveTool.editorSession, moveTool.tr("Finish Move"), *args, **kwargs)
self.movingSchematic = movingSchematic
self.moveTool = moveTool
def undo(self):
super(MoveFinishCommand, self).undo()
self.moveTool.movingSchematic = self.movingSchematic
self.moveTool.movePosition = self.movePosition
self.editorSession.currentSelection = self.previousSelection
self.editorSession.chooseTool("Move")
def redo(self):
super(MoveFinishCommand, self).redo()
self.previousSelection = self.editorSession.currentSelection
self.movePosition = self.moveTool.movePosition
self.editorSession.currentSelection = BoundingBox(self.movePosition, self.previousSelection.size)
self.moveTool.movingSchematic = None
class CoordinateWidget(QtGui.QWidget):
def __init__(self, *args, **kwargs):
@ -86,7 +145,6 @@ class MoveTool(EditorTool):
self.movingWorldScene = None
self.loader = None
self.currentCommand = None
self.dragStartFace = None
self.dragStartPoint = None
@ -116,6 +174,13 @@ class MoveTool(EditorTool):
self.pointInput.point = value
self.pointInputChanged(value)
def doMoveOffsetCommand(self, oldPoint, newPoint):
if newPoint != oldPoint:
command = MoveOffsetCommand(self, oldPoint, newPoint)
self.editorSession.removeUndoBlock(self.moveUndoBlock)
self.editorSession.pushCommand(command)
self.editorSession.setUndoBlock(self.moveUndoBlock)
def pointInputChanged(self, value):
self._movePosition = value
if value is not None:
@ -185,7 +250,7 @@ class MoveTool(EditorTool):
def mouseMove(self, event):
# Hilite face cursor is over
if self.movingSchematic is None:
if self.movingSchematic is None or self.movePosition is None:
return
point, face = boxFaceUnderCursor(self.schematicBox, event.ray)
@ -229,26 +294,31 @@ class MoveTool(EditorTool):
# Don't paste cut selection in yet. Wait for tool switch or "Confirm" button press. Begin new revision
# for paste operation, paste stored world, store revision after paste (should be previously stored revision
# +2), commit MoveCommand to undo history.
pass
if self.movingSchematic is not None:
self.doMoveOffsetCommand(self.dragStartMovePosition, self.movePosition)
def toolActive(self):
self.editorSession.selectionTool.hideSelectionWalls = True
if self.currentCommand is None and self.movingSchematic is None:
if self.movingSchematic is None:
# Need to cut out selection
# xxxx for huge selections, don't cut, just do everything at the end?
if self.editorSession.currentSelection is None:
return
export = self.editorSession.currentDimension.exportSchematicIter(self.editorSession.currentSelection)
self.movingSchematic = showProgress("Lifting...", export)
self.movePosition = self.editorSession.currentSelection.origin
self.currentCommand = SimpleRevisionCommand(self.editorSession, self.tr("Move"))
self.currentCommand.previousRevision = self.editorSession.currentRevision
self.editorSession.beginUndo()
fill = self.editorSession.currentDimension.fillBlocksIter(self.editorSession.currentSelection, "air")
showProgress("Lifting...", fill)
self.editorSession.commitUndo()
self.editorSession.setUndoBlock(self.completeMove)
moveCommand = MoveSelectionCommand(self, self.movingSchematic)
with moveCommand.begin():
fill = self.editorSession.currentDimension.fillBlocksIter(self.editorSession.currentSelection, "air")
showProgress("Lifting...", fill)
self.editorSession.pushCommand(moveCommand)
self.editorSession.setUndoBlock(self.moveUndoBlock)
def moveUndoBlock(self):
self.completeMove()
self.movingSchematic = None
def toolInactive(self):
self.editorSession.selectionTool.hideSelectionWalls = False
@ -263,17 +333,16 @@ class MoveTool(EditorTool):
if self.movingSchematic is None:
return
if self.currentCommand is None:
self.currentCommand = SimpleRevisionCommand(self.editorSession, self.tr("Paste"))
self.currentCommand.previousRevision = self.editorSession.currentRevision
self.editorSession.removeUndoBlock(self.moveUndoBlock)
command = MoveFinishCommand(self, self.movingSchematic)
self.editorSession.removeUndoBlock(self.completeMove)
self.editorSession.beginUndo()
task = self.editorSession.currentDimension.importSchematicIter(self.movingSchematic, self.movePosition)
showProgress(self.tr("Pasting..."), task)
self.editorSession.commitUndo()
with command.begin():
task = self.editorSession.currentDimension.importSchematicIter(self.movingSchematic, self.movePosition)
showProgress(self.tr("Pasting..."), task)
self.currentCommand.currentRevision = self.editorSession.worldEditor.currentRevision
self.editorSession.pushCommand(self.currentCommand)
self.currentCommand = None
self.movingSchematic = None
self.editorSession.pushCommand(command)
def pasteSchematic(self, copiedSchematic, position):
command = MoveSelectionCommand(self, copiedSchematic, position, self.tr("Paste"))
self.editorSession.pushCommand(command)
self.editorSession.setUndoBlock(self.moveUndoBlock)

View File

@ -11,13 +11,18 @@ class MCEUndoStack(QtGui.QUndoStack):
undoBlock = None
def undo(self):
self.clearUndoBlock(False)
self.clearUndoBlock()
super(MCEUndoStack, self).undo()
def redo(self):
self.clearUndoBlock(False) # Shouldn't ever find a block
self.clearUndoBlock() # Shouldn't ever find a block
super(MCEUndoStack, self).redo()
def push(self, *args, **kwargs):
self.clearUndoBlock()
super(MCEUndoStack, self).push(*args, **kwargs)
def setUndoBlock(self, callback):
"""
Set a function to be called before the next time undo, redo, or beginUndo is called. Some tools may need to
@ -42,17 +47,15 @@ class MCEUndoStack(QtGui.QUndoStack):
self.undoBlock, callback)
self.undoBlock = None
def clearUndoBlock(self, complete):
def clearUndoBlock(self):
"""
If an undo block is set, calls its callback and removes it.
:param complete: Whether to complete or abort the command which set the undo block
:type complete: bool
:return:
:rtype:
"""
if self.undoBlock:
callback = self.undoBlock
self.undoBlock = None
callback(complete)
callback()

View File

@ -402,12 +402,15 @@ class WorldView(QGLWidget):
Parameters:
x and y are coordinates local to this QWidget
Returns a Ray
:rtype: Ray
"""
p0, p1 = self.pointsAtPositions((x, y, 0.0), (x, y, 0.1))
return Ray(p0, (p1 - p0).normalize())
def rayAtCenter(self):
return self.rayAtPosition(self.width()/2, self.height()/2)
def pointsAtPositions(self, *screenPoints):
w = float(self.width())
h = float(self.height())

View File

@ -284,27 +284,33 @@ class BoundingBox(SelectionBox):
@property
def origin(self):
"The smallest position in the box"
"""
The smallest position in the box
:rtype: Vector
"""
return self._origin
@property
def size(self):
"The size of the box"
"""
The size of the box
:rtype: Vector
"""
return self._size
@property
def width(self):
"The dimension along the X axis"
"""The dimension along the X axis"""
return self._size.x
@property
def height(self):
"The dimension along the Y axis"
"""The dimension along the Y axis"""
return self._size.y
@property
def length(self):
"The dimension along the Z axis"
"""The dimension along the Z axis"""
return self._size.z
@property

View File

@ -226,6 +226,7 @@ class WorldEditor(object):
:return:
:rtype:
"""
assert index is not None, "None is not a revision index!"
self.syncToDisk()
changes = self.adapter.selectRevision(index)
@ -508,6 +509,11 @@ class WorldEditorDimension(object):
@property
def bounds(self):
"""
:return:
:rtype: BoundingBox
"""
if self._bounds is None:
if hasattr(self.adapter, "getDimensionBounds"):
self._bounds = self.adapter.getDimensionBounds(self.dimName)